From 8f3b8cf989c9fb3ce44c48892e4b95fd7139c196 Mon Sep 17 00:00:00 2001 From: Joseph Manley Date: Sat, 25 Jul 2020 17:09:22 -0400 Subject: [PATCH] Intial commit --- .github/workflows/test_action.yml | 15 + .gitignore | 115 + LICENSE | 7 + action.yml | 16 + dist/index.js | 32984 ++++++++++++++++ dist/pagent.exe | Bin 0 -> 50688 bytes main.js | 48 + package-lock.json | 233 + package.json | 26 + test_proj/.gutconfig.json | 10 + ...n.png-487276ed1e3a0c39cad0279d744ee560.md5 | 3 + ....png-487276ed1e3a0c39cad0279d744ee560.stex | Bin 0 -> 3413 bytes ...n.png-91b084043b8aaf2f1c906e7b9fa92969.md5 | 3 + ....png-91b084043b8aaf2f1c906e7b9fa92969.stex | Bin 0 -> 189 bytes test_proj/Gut.tscn | 40 + test_proj/addons/gut/GutScene.gd | 432 + test_proj/addons/gut/GutScene.tscn | 471 + test_proj/addons/gut/LICENSE.md | 22 + test_proj/addons/gut/UserFileViewer.gd | 55 + test_proj/addons/gut/UserFileViewer.tscn | 127 + test_proj/addons/gut/autofree.gd | 59 + .../double_templates/function_template.txt | 6 + .../gut/double_templates/script_template.txt | 41 + test_proj/addons/gut/doubler.gd | 556 + .../addons/gut/fonts/AnonymousPro-Bold.ttf | Bin 0 -> 107624 bytes .../gut/fonts/AnonymousPro-BoldItalic.ttf | Bin 0 -> 95872 bytes .../addons/gut/fonts/AnonymousPro-Italic.ttf | Bin 0 -> 98424 bytes .../addons/gut/fonts/AnonymousPro-Regular.ttf | Bin 0 -> 112072 bytes .../addons/gut/fonts/CourierPrime-Bold.ttf | Bin 0 -> 69768 bytes .../gut/fonts/CourierPrime-BoldItalic.ttf | Bin 0 -> 77384 bytes .../addons/gut/fonts/CourierPrime-Italic.ttf | Bin 0 -> 76496 bytes .../addons/gut/fonts/CourierPrime-Regular.ttf | Bin 0 -> 68128 bytes .../addons/gut/fonts/LobsterTwo-Bold.ttf | Bin 0 -> 222040 bytes .../gut/fonts/LobsterTwo-BoldItalic.ttf | Bin 0 -> 216036 bytes .../addons/gut/fonts/LobsterTwo-Italic.ttf | Bin 0 -> 229404 bytes .../addons/gut/fonts/LobsterTwo-Regular.ttf | Bin 0 -> 234956 bytes test_proj/addons/gut/fonts/OFL.txt | 94 + test_proj/addons/gut/gut.gd | 1463 + test_proj/addons/gut/gut_cmdln.gd | 402 + test_proj/addons/gut/gut_plugin.gd | 12 + test_proj/addons/gut/hook_script.gd | 35 + test_proj/addons/gut/icon.png | Bin 0 -> 320 bytes test_proj/addons/gut/icon.png.import | 34 + test_proj/addons/gut/logger.gd | 348 + test_proj/addons/gut/method_maker.gd | 213 + test_proj/addons/gut/one_to_many.gd | 38 + test_proj/addons/gut/optparse.gd | 248 + test_proj/addons/gut/orphan_counter.gd | 55 + test_proj/addons/gut/parameter_factory.gd | 75 + test_proj/addons/gut/parameter_handler.gd | 37 + test_proj/addons/gut/plugin.cfg | 7 + test_proj/addons/gut/plugin_control.gd | 247 + test_proj/addons/gut/printers.gd | 157 + test_proj/addons/gut/signal_watcher.gd | 166 + test_proj/addons/gut/source_code_pro.fnt | Bin 0 -> 26499 bytes test_proj/addons/gut/spy.gd | 96 + test_proj/addons/gut/strutils.gd | 120 + test_proj/addons/gut/stub_params.gd | 43 + test_proj/addons/gut/stubber.gd | 163 + test_proj/addons/gut/summary.gd | 172 + test_proj/addons/gut/test.gd | 1219 + test_proj/addons/gut/test_collector.gd | 282 + test_proj/addons/gut/thing_counter.gd | 43 + test_proj/addons/gut/utils.gd | 312 + test_proj/default_env.tres | 7 + test_proj/icon.png | Bin 0 -> 3305 bytes test_proj/icon.png.import | 34 + test_proj/project.godot | 28 + test_proj/tests/test_test.gd | 4 + 69 files changed, 41423 insertions(+) create mode 100644 .github/workflows/test_action.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 action.yml create mode 100644 dist/index.js create mode 100644 dist/pagent.exe create mode 100644 main.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 test_proj/.gutconfig.json create mode 100644 test_proj/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5 create mode 100644 test_proj/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex create mode 100644 test_proj/.import/icon.png-91b084043b8aaf2f1c906e7b9fa92969.md5 create mode 100644 test_proj/.import/icon.png-91b084043b8aaf2f1c906e7b9fa92969.stex create mode 100644 test_proj/Gut.tscn create mode 100644 test_proj/addons/gut/GutScene.gd create mode 100644 test_proj/addons/gut/GutScene.tscn create mode 100644 test_proj/addons/gut/LICENSE.md create mode 100644 test_proj/addons/gut/UserFileViewer.gd create mode 100644 test_proj/addons/gut/UserFileViewer.tscn create mode 100644 test_proj/addons/gut/autofree.gd create mode 100644 test_proj/addons/gut/double_templates/function_template.txt create mode 100644 test_proj/addons/gut/double_templates/script_template.txt create mode 100644 test_proj/addons/gut/doubler.gd create mode 100644 test_proj/addons/gut/fonts/AnonymousPro-Bold.ttf create mode 100644 test_proj/addons/gut/fonts/AnonymousPro-BoldItalic.ttf create mode 100644 test_proj/addons/gut/fonts/AnonymousPro-Italic.ttf create mode 100644 test_proj/addons/gut/fonts/AnonymousPro-Regular.ttf create mode 100644 test_proj/addons/gut/fonts/CourierPrime-Bold.ttf create mode 100644 test_proj/addons/gut/fonts/CourierPrime-BoldItalic.ttf create mode 100644 test_proj/addons/gut/fonts/CourierPrime-Italic.ttf create mode 100644 test_proj/addons/gut/fonts/CourierPrime-Regular.ttf create mode 100644 test_proj/addons/gut/fonts/LobsterTwo-Bold.ttf create mode 100644 test_proj/addons/gut/fonts/LobsterTwo-BoldItalic.ttf create mode 100644 test_proj/addons/gut/fonts/LobsterTwo-Italic.ttf create mode 100644 test_proj/addons/gut/fonts/LobsterTwo-Regular.ttf create mode 100644 test_proj/addons/gut/fonts/OFL.txt create mode 100644 test_proj/addons/gut/gut.gd create mode 100644 test_proj/addons/gut/gut_cmdln.gd create mode 100644 test_proj/addons/gut/gut_plugin.gd create mode 100644 test_proj/addons/gut/hook_script.gd create mode 100644 test_proj/addons/gut/icon.png create mode 100644 test_proj/addons/gut/icon.png.import create mode 100644 test_proj/addons/gut/logger.gd create mode 100644 test_proj/addons/gut/method_maker.gd create mode 100644 test_proj/addons/gut/one_to_many.gd create mode 100644 test_proj/addons/gut/optparse.gd create mode 100644 test_proj/addons/gut/orphan_counter.gd create mode 100644 test_proj/addons/gut/parameter_factory.gd create mode 100644 test_proj/addons/gut/parameter_handler.gd create mode 100644 test_proj/addons/gut/plugin.cfg create mode 100644 test_proj/addons/gut/plugin_control.gd create mode 100644 test_proj/addons/gut/printers.gd create mode 100644 test_proj/addons/gut/signal_watcher.gd create mode 100644 test_proj/addons/gut/source_code_pro.fnt create mode 100644 test_proj/addons/gut/spy.gd create mode 100644 test_proj/addons/gut/strutils.gd create mode 100644 test_proj/addons/gut/stub_params.gd create mode 100644 test_proj/addons/gut/stubber.gd create mode 100644 test_proj/addons/gut/summary.gd create mode 100644 test_proj/addons/gut/test.gd create mode 100644 test_proj/addons/gut/test_collector.gd create mode 100644 test_proj/addons/gut/thing_counter.gd create mode 100644 test_proj/addons/gut/utils.gd create mode 100644 test_proj/default_env.tres create mode 100644 test_proj/icon.png create mode 100644 test_proj/icon.png.import create mode 100644 test_proj/project.godot create mode 100644 test_proj/tests/test_test.gd diff --git a/.github/workflows/test_action.yml b/.github/workflows/test_action.yml new file mode 100644 index 0000000..0d9f443 --- /dev/null +++ b/.github/workflows/test_action.yml @@ -0,0 +1,15 @@ +name: Test Action + +on: [push, pull_request] + +jobs: + TestAction: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Run Action + id: run_tests + uses: ./ + with: + directory: test_proj \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be52143 --- /dev/null +++ b/.gitignore @@ -0,0 +1,115 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5ef0b4c --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2020 Joseph Manley + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..f649c7a --- /dev/null +++ b/action.yml @@ -0,0 +1,16 @@ +name: "Run GUT Tests" +description: "Run GUT tests for a Godot project" +author: "josephbmanley" +inputs: + containerImage: + description: "The container to run tests inside of." + default: "barichello/godot-ci:latest" + directory: + description: "The name directory to run tests in." + required: false +runs: + using: "node12" + main: "dist/index.js" +branding: + icon: cpu + color: yellow \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..c0a4f5d --- /dev/null +++ b/dist/index.js @@ -0,0 +1,32984 @@ +module.exports = +/******/ (function(modules, runtime) { // webpackBootstrap +/******/ "use strict"; +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ var threw = true; +/******/ try { +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ threw = false; +/******/ } finally { +/******/ if(threw) delete installedModules[moduleId]; +/******/ } +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ __webpack_require__.ab = __dirname + "/"; +/******/ +/******/ // the startup function +/******/ function startup() { +/******/ // Load entry module and return exports +/******/ return __webpack_require__(334); +/******/ }; +/******/ +/******/ // run startup +/******/ return startup(); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ 9: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var once = __webpack_require__(49); + +var noop = function() {}; + +var isRequest = function(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +}; + +var isChildProcess = function(stream) { + return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3 +}; + +var eos = function(stream, opts, callback) { + if (typeof opts === 'function') return eos(stream, null, opts); + if (!opts) opts = {}; + + callback = once(callback || noop); + + var ws = stream._writableState; + var rs = stream._readableState; + var readable = opts.readable || (opts.readable !== false && stream.readable); + var writable = opts.writable || (opts.writable !== false && stream.writable); + var cancelled = false; + + var onlegacyfinish = function() { + if (!stream.writable) onfinish(); + }; + + var onfinish = function() { + writable = false; + if (!readable) callback.call(stream); + }; + + var onend = function() { + readable = false; + if (!writable) callback.call(stream); + }; + + var onexit = function(exitCode) { + callback.call(stream, exitCode ? new Error('exited with error code: ' + exitCode) : null); + }; + + var onerror = function(err) { + callback.call(stream, err); + }; + + var onclose = function() { + process.nextTick(onclosenexttick); + }; + + var onclosenexttick = function() { + if (cancelled) return; + if (readable && !(rs && (rs.ended && !rs.destroyed))) return callback.call(stream, new Error('premature close')); + if (writable && !(ws && (ws.ended && !ws.destroyed))) return callback.call(stream, new Error('premature close')); + }; + + var onrequest = function() { + stream.req.on('finish', onfinish); + }; + + if (isRequest(stream)) { + stream.on('complete', onfinish); + stream.on('abort', onclose); + if (stream.req) onrequest(); + else stream.on('request', onrequest); + } else if (writable && !ws) { // legacy streams + stream.on('end', onlegacyfinish); + stream.on('close', onlegacyfinish); + } + + if (isChildProcess(stream)) stream.on('exit', onexit); + + stream.on('end', onend); + stream.on('finish', onfinish); + if (opts.error !== false) stream.on('error', onerror); + stream.on('close', onclose); + + return function() { + cancelled = true; + stream.removeListener('complete', onfinish); + stream.removeListener('abort', onclose); + stream.removeListener('request', onrequest); + if (stream.req) stream.req.removeListener('finish', onfinish); + stream.removeListener('end', onlegacyfinish); + stream.removeListener('close', onlegacyfinish); + stream.removeListener('finish', onfinish); + stream.removeListener('exit', onexit); + stream.removeListener('end', onend); + stream.removeListener('error', onerror); + stream.removeListener('close', onclose); + }; +}; + +module.exports = eos; + + +/***/ }), + +/***/ 11: +/***/ (function(module) { + +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) + + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') + + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) + + return wrapper + + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] + }) + } + return ret + } +} + + +/***/ }), + +/***/ 23: +/***/ (function(module, __unusedexports, __webpack_require__) { + +module.exports = HttpDuplex; + +var util = __webpack_require__(669), + stream = __webpack_require__(574); + +util.inherits(HttpDuplex, stream.Duplex); + +function HttpDuplex(req, res, options) { + var self = this; + + if (!(self instanceof HttpDuplex)) return new HttpDuplex(req, res, options); + + stream.Duplex.call(self, options); + self._output = null; + + self.connect(req, res); +} + +HttpDuplex.prototype.connect = function(req, res) { + var self = this; + self.req = req; + self._output = res; + self.emit('response', res); + + res.on('data', function(c) { + if (!self.push(c)) self._output.pause(); + }); + res.on('end', function() { + self.push(null); + }); +}; + +HttpDuplex.prototype._read = function(n) { + if (this._output) this._output.resume(); +}; + +HttpDuplex.prototype._write = function(chunk, encoding, cb) { + this.req.write(chunk, encoding); + cb(); +}; + +HttpDuplex.prototype.end = function(chunk, encoding, cb) { + this._output.socket.destroy(); + return this.req.end(chunk, encoding, cb); +}; + +HttpDuplex.prototype.destroy = function() { + this.req.destroy(); + this._output.socket.destroy(); +}; + + +/***/ }), + +/***/ 33: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// This wrapper class is used to retain backwards compatibility with +// pre-v0.4 ssh2. If it weren't for `read()` and `write()` being used by the +// streams2/3 API, we could just pass the SFTPStream directly to the end user... + +var inherits = __webpack_require__(669).inherits; +var EventEmitter = __webpack_require__(614).EventEmitter; + +function SFTPWrapper(stream) { + var self = this; + + EventEmitter.call(this); + + this._stream = stream; + + stream.on('error', function(err) { + self.emit('error', err); + }).on('end', function() { + self.emit('end'); + }).on('close', function() { + self.emit('close'); + }).on('continue', function() { + self.emit('continue'); + }); +} +inherits(SFTPWrapper, EventEmitter); + +// stream-related methods to pass on +SFTPWrapper.prototype.end = function() { + return this._stream.end(); +}; +// SFTPStream client methods +SFTPWrapper.prototype.createReadStream = function(path, options) { + return this._stream.createReadStream(path, options); +}; +SFTPWrapper.prototype.createWriteStream = function(path, options) { + return this._stream.createWriteStream(path, options); +}; +SFTPWrapper.prototype.open = function(path, flags, attrs, cb) { + return this._stream.open(path, flags, attrs, cb); +}; +SFTPWrapper.prototype.close = function(handle, cb) { + return this._stream.close(handle, cb); +}; +SFTPWrapper.prototype.read = function(handle, buf, off, len, position, cb) { + return this._stream.readData(handle, buf, off, len, position, cb); +}; +SFTPWrapper.prototype.write = function(handle, buf, off, len, position, cb) { + return this._stream.writeData(handle, buf, off, len, position, cb); +}; +SFTPWrapper.prototype.fastGet = function(remotePath, localPath, opts, cb) { + return this._stream.fastGet(remotePath, localPath, opts, cb); +}; +SFTPWrapper.prototype.fastPut = function(localPath, remotePath, opts, cb) { + return this._stream.fastPut(localPath, remotePath, opts, cb); +}; +SFTPWrapper.prototype.readFile = function(path, options, callback_) { + return this._stream.readFile(path, options, callback_); +}; +SFTPWrapper.prototype.writeFile = function(path, data, options, callback_) { + return this._stream.writeFile(path, data, options, callback_); +}; +SFTPWrapper.prototype.appendFile = function(path, data, options, callback_) { + return this._stream.appendFile(path, data, options, callback_); +}; +SFTPWrapper.prototype.exists = function(path, cb) { + return this._stream.exists(path, cb); +}; +SFTPWrapper.prototype.unlink = function(filename, cb) { + return this._stream.unlink(filename, cb); +}; +SFTPWrapper.prototype.rename = function(oldPath, newPath, cb) { + return this._stream.rename(oldPath, newPath, cb); +}; +SFTPWrapper.prototype.mkdir = function(path, attrs, cb) { + return this._stream.mkdir(path, attrs, cb); +}; +SFTPWrapper.prototype.rmdir = function(path, cb) { + return this._stream.rmdir(path, cb); +}; +SFTPWrapper.prototype.readdir = function(where, opts, cb) { + return this._stream.readdir(where, opts, cb); +}; +SFTPWrapper.prototype.fstat = function(handle, cb) { + return this._stream.fstat(handle, cb); +}; +SFTPWrapper.prototype.stat = function(path, cb) { + return this._stream.stat(path, cb); +}; +SFTPWrapper.prototype.lstat = function(path, cb) { + return this._stream.lstat(path, cb); +}; +SFTPWrapper.prototype.opendir = function(path, cb) { + return this._stream.opendir(path, cb); +}; +SFTPWrapper.prototype.setstat = function(path, attrs, cb) { + return this._stream.setstat(path, attrs, cb); +}; +SFTPWrapper.prototype.fsetstat = function(handle, attrs, cb) { + return this._stream.fsetstat(handle, attrs, cb); +}; +SFTPWrapper.prototype.futimes = function(handle, atime, mtime, cb) { + return this._stream.futimes(handle, atime, mtime, cb); +}; +SFTPWrapper.prototype.utimes = function(path, atime, mtime, cb) { + return this._stream.utimes(path, atime, mtime, cb); +}; +SFTPWrapper.prototype.fchown = function(handle, uid, gid, cb) { + return this._stream.fchown(handle, uid, gid, cb); +}; +SFTPWrapper.prototype.chown = function(path, uid, gid, cb) { + return this._stream.chown(path, uid, gid, cb); +}; +SFTPWrapper.prototype.fchmod = function(handle, mode, cb) { + return this._stream.fchmod(handle, mode, cb); +}; +SFTPWrapper.prototype.chmod = function(path, mode, cb) { + return this._stream.chmod(path, mode, cb); +}; +SFTPWrapper.prototype.readlink = function(path, cb) { + return this._stream.readlink(path, cb); +}; +SFTPWrapper.prototype.symlink = function(targetPath, linkPath, cb) { + return this._stream.symlink(targetPath, linkPath, cb); +}; +SFTPWrapper.prototype.realpath = function(path, cb) { + return this._stream.realpath(path, cb); +}; +// extended requests +SFTPWrapper.prototype.ext_openssh_rename = function(oldPath, newPath, cb) { + return this._stream.ext_openssh_rename(oldPath, newPath, cb); +}; +SFTPWrapper.prototype.ext_openssh_statvfs = function(path, cb) { + return this._stream.ext_openssh_statvfs(path, cb); +}; +SFTPWrapper.prototype.ext_openssh_fstatvfs = function(handle, cb) { + return this._stream.ext_openssh_fstatvfs(handle, cb); +}; +SFTPWrapper.prototype.ext_openssh_hardlink = function(oldPath, newPath, cb) { + return this._stream.ext_openssh_hardlink(oldPath, newPath, cb); +}; +SFTPWrapper.prototype.ext_openssh_fsync = function(handle, cb) { + return this._stream.ext_openssh_fsync(handle, cb); +}; + +module.exports = SFTPWrapper; + + +/***/ }), + +/***/ 46: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var _Object$setPrototypeO; + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +var finished = __webpack_require__(740); + +var kLastResolve = Symbol('lastResolve'); +var kLastReject = Symbol('lastReject'); +var kError = Symbol('error'); +var kEnded = Symbol('ended'); +var kLastPromise = Symbol('lastPromise'); +var kHandlePromise = Symbol('handlePromise'); +var kStream = Symbol('stream'); + +function createIterResult(value, done) { + return { + value: value, + done: done + }; +} + +function readAndResolve(iter) { + var resolve = iter[kLastResolve]; + + if (resolve !== null) { + var data = iter[kStream].read(); // we defer if data is null + // we can be expecting either 'end' or + // 'error' + + if (data !== null) { + iter[kLastPromise] = null; + iter[kLastResolve] = null; + iter[kLastReject] = null; + resolve(createIterResult(data, false)); + } + } +} + +function onReadable(iter) { + // we wait for the next tick, because it might + // emit an error with process.nextTick + process.nextTick(readAndResolve, iter); +} + +function wrapForNext(lastPromise, iter) { + return function (resolve, reject) { + lastPromise.then(function () { + if (iter[kEnded]) { + resolve(createIterResult(undefined, true)); + return; + } + + iter[kHandlePromise](resolve, reject); + }, reject); + }; +} + +var AsyncIteratorPrototype = Object.getPrototypeOf(function () {}); +var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = { + get stream() { + return this[kStream]; + }, + + next: function next() { + var _this = this; + + // if we have detected an error in the meanwhile + // reject straight away + var error = this[kError]; + + if (error !== null) { + return Promise.reject(error); + } + + if (this[kEnded]) { + return Promise.resolve(createIterResult(undefined, true)); + } + + if (this[kStream].destroyed) { + // We need to defer via nextTick because if .destroy(err) is + // called, the error will be emitted via nextTick, and + // we cannot guarantee that there is no error lingering around + // waiting to be emitted. + return new Promise(function (resolve, reject) { + process.nextTick(function () { + if (_this[kError]) { + reject(_this[kError]); + } else { + resolve(createIterResult(undefined, true)); + } + }); + }); + } // if we have multiple next() calls + // we will wait for the previous Promise to finish + // this logic is optimized to support for await loops, + // where next() is only called once at a time + + + var lastPromise = this[kLastPromise]; + var promise; + + if (lastPromise) { + promise = new Promise(wrapForNext(lastPromise, this)); + } else { + // fast path needed to support multiple this.push() + // without triggering the next() queue + var data = this[kStream].read(); + + if (data !== null) { + return Promise.resolve(createIterResult(data, false)); + } + + promise = new Promise(this[kHandlePromise]); + } + + this[kLastPromise] = promise; + return promise; + } +}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () { + return this; +}), _defineProperty(_Object$setPrototypeO, "return", function _return() { + var _this2 = this; + + // destroy(err, cb) is a private API + // we can guarantee we have that here, because we control the + // Readable class this is attached to + return new Promise(function (resolve, reject) { + _this2[kStream].destroy(null, function (err) { + if (err) { + reject(err); + return; + } + + resolve(createIterResult(undefined, true)); + }); + }); +}), _Object$setPrototypeO), AsyncIteratorPrototype); + +var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) { + var _Object$create; + + var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, { + value: stream, + writable: true + }), _defineProperty(_Object$create, kLastResolve, { + value: null, + writable: true + }), _defineProperty(_Object$create, kLastReject, { + value: null, + writable: true + }), _defineProperty(_Object$create, kError, { + value: null, + writable: true + }), _defineProperty(_Object$create, kEnded, { + value: stream._readableState.endEmitted, + writable: true + }), _defineProperty(_Object$create, kHandlePromise, { + value: function value(resolve, reject) { + var data = iterator[kStream].read(); + + if (data) { + iterator[kLastPromise] = null; + iterator[kLastResolve] = null; + iterator[kLastReject] = null; + resolve(createIterResult(data, false)); + } else { + iterator[kLastResolve] = resolve; + iterator[kLastReject] = reject; + } + }, + writable: true + }), _Object$create)); + iterator[kLastPromise] = null; + finished(stream, function (err) { + if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') { + var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise + // returned by next() and store the error + + if (reject !== null) { + iterator[kLastPromise] = null; + iterator[kLastResolve] = null; + iterator[kLastReject] = null; + reject(err); + } + + iterator[kError] = err; + return; + } + + var resolve = iterator[kLastResolve]; + + if (resolve !== null) { + iterator[kLastPromise] = null; + iterator[kLastResolve] = null; + iterator[kLastReject] = null; + resolve(createIterResult(undefined, true)); + } + + iterator[kEnded] = true; + }); + stream.on('readable', onReadable.bind(null, iterator)); + return iterator; +}; + +module.exports = createReadableStreamAsyncIterator; + +/***/ }), + +/***/ 49: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var wrappy = __webpack_require__(11) +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) + + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} + +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) + } + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f +} + + +/***/ }), + +/***/ 58: +/***/ (function(module) { + +module.exports = { + readUInt32BE: function readUInt32BE(buf, offset) { + return buf[offset++] * 16777216 + + buf[offset++] * 65536 + + buf[offset++] * 256 + + buf[offset]; + }, + writeUInt32BE: function writeUInt32BE(buf, value, offset) { + buf[offset++] = (value >>> 24); + buf[offset++] = (value >>> 16); + buf[offset++] = (value >>> 8); + buf[offset++] = value; + return offset; + }, + writeUInt32LE: function writeUInt32LE(buf, value, offset) { + buf[offset++] = value; + buf[offset++] = (value >>> 8); + buf[offset++] = (value >>> 16); + buf[offset++] = (value >>> 24); + return offset; + } +}; + + +/***/ }), + +/***/ 62: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2011 Mark Cavage All rights reserved. + +// If you have no idea what ASN.1 or BER is, see this: +// ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc + +var Ber = __webpack_require__(249); + + + +// --- Exported API + +module.exports = { + + Ber: Ber, + + BerReader: Ber.Reader, + + BerWriter: Ber.Writer + +}; + + +/***/ }), + +/***/ 75: +/***/ (function(module) { + +// https://github.com/HenrikJoreteg/extend-object/blob/v0.1.0/extend-object.js + +var arr = []; +var each = arr.forEach; +var slice = arr.slice; + +module.exports.extend = function(obj) { + each.call(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; +}; + +module.exports.parseJSON = function(s) { + try { + return JSON.parse(s); + } catch (e) { + return null; + } +}; + + +/***/ }), + +/***/ 76: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents an Service + * @param {Object} modem docker-modem + * @param {String} id Service's ID + */ +var Service = function(modem, id) { + this.modem = modem; + this.id = id; +}; + +Service.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Query Docker for service details. + * + * @param {function} callback + */ +Service.prototype.inspect = function(callback) { + var self = this; + + var optsf = { + path: '/services/' + this.id, + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such service', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Delete Service + * + * @param {function} callback + */ +Service.prototype.remove = function(callback) { + var self = this; + + var optsf = { + path: '/services/' + this.id, + method: 'DELETE', + statusCodes: { + 200: true, + 204: true, + 404: 'no such service', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Update service + * + * @param {object} auth + * @param {object} opts + * @param {function} callback + */ +Service.prototype.update = function(auth, opts, callback) { + var self = this; + if (!callback) { + var t = typeof opts; + if(t === 'function'){ + callback = opts; + opts = auth; + auth = opts.authconfig || undefined; + } else if (t === 'undefined'){ + opts = auth; + auth = opts.authconfig || undefined; + } + } + + var optsf = { + path: '/services/' + this.id + '/update?', + method: 'POST', + statusCodes: { + 200: true, + 404: 'no such service', + 500: 'server error' + }, + authconfig: auth, + options: opts + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + + + +/** + * Service logs + * @param {Object} opts Logs options. (optional) + * @param {Function} callback Callback with data + */ +Service.prototype.logs = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, {}); + + var optsf = { + path: '/services/' + this.id + '/logs?', + method: 'GET', + isStream: args.opts.follow || false, + statusCodes: { + 200: true, + 404: 'no such service', + 500: 'server error', + 503: 'node is not part of a swarm' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + + +module.exports = Service; + + +/***/ }), + +/***/ 81: +/***/ (function(module, exports, __webpack_require__) { + +/** + * Module dependencies. + */ + +const tty = __webpack_require__(867); +const util = __webpack_require__(669); + +/** + * This is the Node.js implementation of `debug()`. + */ + +exports.init = init; +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +try { + // Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json) + // eslint-disable-next-line import/no-extraneous-dependencies + const supportsColor = __webpack_require__(858); + + if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) { + exports.colors = [ + 20, + 21, + 26, + 27, + 32, + 33, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 56, + 57, + 62, + 63, + 68, + 69, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 92, + 93, + 98, + 99, + 112, + 113, + 128, + 129, + 134, + 135, + 148, + 149, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 178, + 179, + 184, + 185, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 214, + 215, + 220, + 221 + ]; + } +} catch (error) { + // Swallow - we only care if `supports-color` is available; it doesn't have to be. +} + +/** + * Build up the default `inspectOpts` object from the environment variables. + * + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + */ + +exports.inspectOpts = Object.keys(process.env).filter(key => { + return /^debug_/i.test(key); +}).reduce((obj, key) => { + // Camel-case + const prop = key + .substring(6) + .toLowerCase() + .replace(/_([a-z])/g, (_, k) => { + return k.toUpperCase(); + }); + + // Coerce string value into JS value + let val = process.env[key]; + if (/^(yes|on|true|enabled)$/i.test(val)) { + val = true; + } else if (/^(no|off|false|disabled)$/i.test(val)) { + val = false; + } else if (val === 'null') { + val = null; + } else { + val = Number(val); + } + + obj[prop] = val; + return obj; +}, {}); + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + return 'colors' in exports.inspectOpts ? + Boolean(exports.inspectOpts.colors) : + tty.isatty(process.stderr.fd); +} + +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + +function formatArgs(args) { + const {namespace: name, useColors} = this; + + if (useColors) { + const c = this.color; + const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c); + const prefix = ` ${colorCode};1m${name} \u001B[0m`; + + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m'); + } else { + args[0] = getDate() + name + ' ' + args[0]; + } +} + +function getDate() { + if (exports.inspectOpts.hideDate) { + return ''; + } + return new Date().toISOString() + ' '; +} + +/** + * Invokes `util.format()` with the specified arguments and writes to stderr. + */ + +function log(...args) { + return process.stderr.write(util.format(...args) + '\n'); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ +function save(namespaces) { + if (namespaces) { + process.env.DEBUG = namespaces; + } else { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + return process.env.DEBUG; +} + +/** + * Init logic for `debug` instances. + * + * Create a new `inspectOpts` object in case `useColors` is set + * differently for a particular `debug` instance. + */ + +function init(debug) { + debug.inspectOpts = {}; + + const keys = Object.keys(exports.inspectOpts); + for (let i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } +} + +module.exports = __webpack_require__(486)(exports); + +const {formatters} = module.exports; + +/** + * Map %o to `util.inspect()`, all on a single line. + */ + +formatters.o = function (v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts) + .replace(/\s*\n\s*/g, ' '); +}; + +/** + * Map %O to `util.inspect()`, allowing multiple lines if needed. + */ + +formatters.O = function (v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); +}; + + +/***/ }), + +/***/ 87: +/***/ (function(module) { + +module.exports = require("os"); + +/***/ }), + +/***/ 89: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var inherits = __webpack_require__(669).inherits; +var DuplexStream = __webpack_require__(413).Duplex; +var ReadableStream = __webpack_require__(413).Readable; +var WritableStream = __webpack_require__(413).Writable; + +var STDERR = __webpack_require__(139).constants.CHANNEL_EXTENDED_DATATYPE.STDERR; + +var PACKET_SIZE = 32 * 1024; +var MAX_WINDOW = 2 * 1024 * 1024; +var WINDOW_THRESHOLD = MAX_WINDOW / 2; +var CUSTOM_EVENTS = [ + 'CHANNEL_EOF', + 'CHANNEL_CLOSE', + 'CHANNEL_DATA', + 'CHANNEL_EXTENDED_DATA', + 'CHANNEL_WINDOW_ADJUST', + 'CHANNEL_SUCCESS', + 'CHANNEL_FAILURE', + 'CHANNEL_REQUEST' +]; +var CUSTOM_EVENTS_LEN = CUSTOM_EVENTS.length; + +function Channel(info, client, opts) { + var streamOpts = { + highWaterMark: MAX_WINDOW, + allowHalfOpen: (!opts || (opts && opts.allowHalfOpen !== false)) + }; + + this.allowHalfOpen = streamOpts.allowHalfOpen; + + DuplexStream.call(this, streamOpts); + + var self = this; + var server = opts && opts.server; + + this.server = server; + this.type = info.type; + this.subtype = undefined; + /* + incoming and outgoing contain these properties: + { + id: undefined, + window: undefined, + packetSize: undefined, + state: 'closed' + } + */ + var incoming = this.incoming = info.incoming; + var incomingId = incoming.id; + var outgoing = this.outgoing = info.outgoing; + var callbacks = this._callbacks = []; + var exitCode; + var exitSignal; + var exitDump; + var exitDesc; + var exitLang; + + this._client = client; + this._hasX11 = false; + + var channels = client._channels; + var sshstream = client._sshstream; + + function ondrain() { + if (self._waitClientDrain) { + self._waitClientDrain = false; + if (!self._waitWindow) { + if (self._chunk) + self._write(self._chunk, null, self._chunkcb); + else if (self._chunkcb) + self._chunkcb(); + else if (self._chunkErr) + self.stderr._write(self._chunkErr, null, self._chunkcbErr); + else if (self._chunkcbErr) + self._chunkcbErr(); + } + } + } + client._sock.on('drain', ondrain); + + sshstream.once('CHANNEL_EOF:' + incomingId, function() { + if (incoming.state !== 'open') + return; + incoming.state = 'eof'; + + if (self.readable) + self.push(null); + if (!server && self.stderr.readable) + self.stderr.push(null); + }).once('CHANNEL_CLOSE:' + incomingId, function() { + if (incoming.state === 'closed') + return; + incoming.state = 'closed'; + + if (self.readable) + self.push(null); + if (server && self.stderr.writable) + self.stderr.end(); + else if (!server && self.stderr.readable) + self.stderr.push(null); + + if (outgoing.state === 'open' || outgoing.state === 'eof') + self.close(); + if (outgoing.state === 'closing') + outgoing.state = 'closed'; + + delete channels[incomingId]; + + var state = self._writableState; + client._sock.removeListener('drain', ondrain); + if (!state.ending && !state.finished) + self.end(); + + // Take care of any outstanding channel requests + self._callbacks = []; + for (var i = 0; i < callbacks.length; ++i) + callbacks[i](true); + callbacks = self._callbacks; + + if (!server) { + // align more with node child processes, where the close event gets the + // same arguments as the exit event + if (!self.readable) { + if (exitCode === null) { + self.emit('close', exitCode, exitSignal, exitDump, exitDesc, + exitLang); + } else + self.emit('close', exitCode); + } else { + self.once('end', function() { + if (exitCode === null) { + self.emit('close', exitCode, exitSignal, exitDump, exitDesc, + exitLang); + } else + self.emit('close', exitCode); + }); + } + + if (!self.stderr.readable) + self.stderr.emit('close'); + else { + self.stderr.once('end', function() { + self.stderr.emit('close'); + }); + } + } else { // Server mode + if (!self.readable) + self.emit('close'); + else { + self.once('end', function() { + self.emit('close'); + }); + } + } + + for (var i = 0; i < CUSTOM_EVENTS_LEN; ++i) + sshstream.removeAllListeners(CUSTOM_EVENTS[i] + ':' + incomingId); + }).on('CHANNEL_DATA:' + incomingId, function(data) { + // the remote party should not be sending us data if there is no window + // space available ... + // TODO: raise error on data with not enough window + if (incoming.window === 0) + return; + + incoming.window -= data.length; + + if (!self.push(data)) { + self._waitChanDrain = true; + return; + } + + if (incoming.window <= WINDOW_THRESHOLD) + windowAdjust(self); + }).on('CHANNEL_WINDOW_ADJUST:' + incomingId, function(amt) { + // the server is allowing us to send `amt` more bytes of data + outgoing.window += amt; + + if (self._waitWindow) { + self._waitWindow = false; + if (!self._waitClientDrain) { + if (self._chunk) + self._write(self._chunk, null, self._chunkcb); + else if (self._chunkcb) + self._chunkcb(); + else if (self._chunkErr) + self.stderr._write(self._chunkErr, null, self._chunkcbErr); + else if (self._chunkcbErr) + self._chunkcbErr(); + } + } + }).on('CHANNEL_SUCCESS:' + incomingId, function() { + if (server) { + sshstream._kalast = Date.now(); + sshstream._kacnt = 0; + } else + client._resetKA(); + if (callbacks.length) + callbacks.shift()(false); + }).on('CHANNEL_FAILURE:' + incomingId, function() { + if (server) { + sshstream._kalast = Date.now(); + sshstream._kacnt = 0; + } else + client._resetKA(); + if (callbacks.length) + callbacks.shift()(true); + }).on('CHANNEL_REQUEST:' + incomingId, function(info) { + if (!server) { + if (info.request === 'exit-status') { + self.emit('exit', exitCode = info.code); + return; + } else if (info.request === 'exit-signal') { + self.emit('exit', + exitCode = null, + exitSignal = 'SIG' + info.signal, + exitDump = info.coredump, + exitDesc = info.description, + exitLang = info.lang); + return; + } + } + + // keepalive request? OpenSSH will send one as a channel request if there + // is a channel open + + if (info.wantReply) + sshstream.channelFailure(outgoing.id); + }); + + this.stdin = this.stdout = this; + + if (server) + this.stderr = new ServerStderr(this); + else { + this.stderr = new ReadableStream(streamOpts); + this.stderr._read = function(n) { + if (self._waitChanDrain) { + self._waitChanDrain = false; + if (incoming.window <= WINDOW_THRESHOLD) + windowAdjust(self); + } + }; + + sshstream.on('CHANNEL_EXTENDED_DATA:' + incomingId, + function(type, data) { + // the remote party should not be sending us data if there is no window + // space available ... + // TODO: raise error on data with not enough window + if (incoming.window === 0) + return; + + incoming.window -= data.length; + + if (!self.stderr.push(data)) { + self._waitChanDrain = true; + return; + } + + if (incoming.window <= WINDOW_THRESHOLD) + windowAdjust(self); + } + ); + } + + // outgoing data + this._waitClientDrain = false; // Client stream-level backpressure + this._waitWindow = false; // SSH-level backpressure + + // incoming data + this._waitChanDrain = false; // Channel Readable side backpressure + + this._chunk = undefined; + this._chunkcb = undefined; + this._chunkErr = undefined; + this._chunkcbErr = undefined; + + function onFinish() { + self.eof(); + if (server || (!server && !self.allowHalfOpen)) + self.close(); + self.writable = false; + } + this.on('finish', onFinish) + .on('prefinish', onFinish); // for node v0.11+ + function onEnd() { + self.readable = false; + } + this.on('end', onEnd) + .on('close', onEnd); +} +inherits(Channel, DuplexStream); + +Channel.prototype.eof = function() { + var ret = true; + var outgoing = this.outgoing; + + if (outgoing.state === 'open') { + outgoing.state = 'eof'; + ret = this._client._sshstream.channelEOF(outgoing.id); + } + + return ret; +}; + +Channel.prototype.close = function() { + var ret = true; + var outgoing = this.outgoing; + + if (outgoing.state === 'open' || outgoing.state === 'eof') { + outgoing.state = 'closing'; + ret = this._client._sshstream.channelClose(outgoing.id); + } + + return ret; +}; + +Channel.prototype._read = function(n) { + if (this._waitChanDrain) { + this._waitChanDrain = false; + if (this.incoming.window <= WINDOW_THRESHOLD) + windowAdjust(this); + } +}; + +Channel.prototype._write = function(data, encoding, cb) { + var sshstream = this._client._sshstream; + var outgoing = this.outgoing; + var packetSize = outgoing.packetSize; + var id = outgoing.id; + var window = outgoing.window; + var len = data.length; + var p = 0; + var ret; + var buf; + var sliceLen; + + if (outgoing.state !== 'open') + return; + + while (len - p > 0 && window > 0) { + sliceLen = len - p; + if (sliceLen > window) + sliceLen = window; + if (sliceLen > packetSize) + sliceLen = packetSize; + + ret = sshstream.channelData(id, data.slice(p, p + sliceLen)); + + p += sliceLen; + window -= sliceLen; + + if (!ret) { + this._waitClientDrain = true; + this._chunk = undefined; + this._chunkcb = cb; + break; + } + } + + outgoing.window = window; + + if (len - p > 0) { + if (window === 0) + this._waitWindow = true; + if (p > 0) { + // partial + buf = Buffer.allocUnsafe(len - p); + data.copy(buf, 0, p); + this._chunk = buf; + } else + this._chunk = data; + this._chunkcb = cb; + return; + } + + if (!this._waitClientDrain) + cb(); +}; + +Channel.prototype.destroy = function() { + this.end(); +}; + +// session type-specific methods +Channel.prototype.setWindow = function(rows, cols, height, width) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + if (this.type === 'session' + && (this.subtype === 'shell' || this.subtype === 'exec') + && this.writable + && this.outgoing.state === 'open') { + return this._client._sshstream.windowChange(this.outgoing.id, + rows, + cols, + height, + width); + } + + return true; +}; +Channel.prototype.signal = function(signalName) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + if (this.type === 'session' + && this.writable + && this.outgoing.state === 'open') + return this._client._sshstream.signal(this.outgoing.id, signalName); + + return true; +}; +Channel.prototype.exit = function(name, coreDumped, msg) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + if (this.type === 'session' + && this.writable + && this.outgoing.state === 'open') { + if (typeof name === 'number') + return this._client._sshstream.exitStatus(this.outgoing.id, name); + else { + return this._client._sshstream.exitSignal(this.outgoing.id, + name, + coreDumped, + msg); + } + } + + return true; +}; + +Channel.MAX_WINDOW = MAX_WINDOW; +Channel.PACKET_SIZE = PACKET_SIZE; + +function windowAdjust(self) { + if (self.outgoing.state === 'closed') + return true; + var amt = MAX_WINDOW - self.incoming.window; + if (amt <= 0) + return true; + self.incoming.window += amt; + return self._client._sshstream.channelWindowAdjust(self.outgoing.id, amt); +} + +function ServerStderr(channel) { + WritableStream.call(this, { highWaterMark: MAX_WINDOW }); + this._channel = channel; +} +inherits(ServerStderr, WritableStream); + +ServerStderr.prototype._write = function(data, encoding, cb) { + var channel = this._channel; + var sshstream = channel._client._sshstream; + var outgoing = channel.outgoing; + var packetSize = outgoing.packetSize; + var id = outgoing.id; + var window = outgoing.window; + var len = data.length; + var p = 0; + var ret; + var buf; + var sliceLen; + + if (channel.outgoing.state !== 'open') + return; + + while (len - p > 0 && window > 0) { + sliceLen = len - p; + if (sliceLen > window) + sliceLen = window; + if (sliceLen > packetSize) + sliceLen = packetSize; + + ret = sshstream.channelExtData(id, data.slice(p, p + sliceLen), STDERR); + + p += sliceLen; + window -= sliceLen; + + if (!ret) { + channel._waitClientDrain = true; + channel._chunkErr = undefined; + channel._chunkcbErr = cb; + break; + } + } + + outgoing.window = window; + + if (len - p > 0) { + if (window === 0) + channel._waitWindow = true; + if (p > 0) { + // partial + buf = Buffer.allocUnsafe(len - p); + data.copy(buf, 0, p); + channel._chunkErr = buf; + } else + channel._chunkErr = data; + channel._chunkcbErr = cb; + return; + } + + if (!channel._waitClientDrain) + cb(); +}; + +module.exports = Channel; + + +/***/ }), + +/***/ 107: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents an Node + * @param {Object} modem docker-modem + * @param {String} id Node's ID + */ +var Node = function(modem, id) { + this.modem = modem; + this.id = id; +}; + +Node.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Query Docker for Node details. + * + * @param {function} callback + */ +Node.prototype.inspect = function(callback) { + var self = this; + + var optsf = { + path: '/nodes/' + this.id, + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such node', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + + +/** + * Update a node. + * + * @param {object} opts + * @param {function} callback + */ +Node.prototype.update = function(opts, callback) { + var self = this; + if (!callback && typeof opts === 'function') { + callback = opts; + } + + var optsf = { + path: '/nodes/' + this.id + '/update?', + method: 'POST', + statusCodes: { + 200: true, + 404: 'no such node', + 406: 'node is not part of a swarm', + 500: 'server error' + }, + options: opts + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + + +/** + * Remove a Node. + * Warning: This method is not documented in the API. + * + * @param {object} opts + * @param {function} callback + */ +Node.prototype.remove = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/nodes/' + this.id + '?', + method: 'DELETE', + statusCodes: { + 200: true, + 404: 'no such node', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + +module.exports = Node; + + +/***/ }), + +/***/ 120: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +var chownr = __webpack_require__(941) +var tar = __webpack_require__(394) +var pump = __webpack_require__(453) +var mkdirp = __webpack_require__(516) +var fs = __webpack_require__(747) +var path = __webpack_require__(622) +var os = __webpack_require__(87) + +var win32 = os.platform() === 'win32' + +var noop = function () {} + +var echo = function (name) { + return name +} + +var normalize = !win32 ? echo : function (name) { + return name.replace(/\\/g, '/').replace(/[:?<>|]/g, '_') +} + +var statAll = function (fs, stat, cwd, ignore, entries, sort) { + var queue = entries || ['.'] + + return function loop (callback) { + if (!queue.length) return callback() + var next = queue.shift() + var nextAbs = path.join(cwd, next) + + stat(nextAbs, function (err, stat) { + if (err) return callback(err) + + if (!stat.isDirectory()) return callback(null, next, stat) + + fs.readdir(nextAbs, function (err, files) { + if (err) return callback(err) + + if (sort) files.sort() + for (var i = 0; i < files.length; i++) { + if (!ignore(path.join(cwd, next, files[i]))) queue.push(path.join(next, files[i])) + } + + callback(null, next, stat) + }) + }) + } +} + +var strip = function (map, level) { + return function (header) { + header.name = header.name.split('/').slice(level).join('/') + + var linkname = header.linkname + if (linkname && (header.type === 'link' || path.isAbsolute(linkname))) { + header.linkname = linkname.split('/').slice(level).join('/') + } + + return map(header) + } +} + +exports.pack = function (cwd, opts) { + if (!cwd) cwd = '.' + if (!opts) opts = {} + + var xfs = opts.fs || fs + var ignore = opts.ignore || opts.filter || noop + var map = opts.map || noop + var mapStream = opts.mapStream || echo + var statNext = statAll(xfs, opts.dereference ? xfs.stat : xfs.lstat, cwd, ignore, opts.entries, opts.sort) + var strict = opts.strict !== false + var umask = typeof opts.umask === 'number' ? ~opts.umask : ~processUmask() + var dmode = typeof opts.dmode === 'number' ? opts.dmode : 0 + var fmode = typeof opts.fmode === 'number' ? opts.fmode : 0 + var pack = opts.pack || tar.pack() + var finish = opts.finish || noop + + if (opts.strip) map = strip(map, opts.strip) + + if (opts.readable) { + dmode |= parseInt(555, 8) + fmode |= parseInt(444, 8) + } + if (opts.writable) { + dmode |= parseInt(333, 8) + fmode |= parseInt(222, 8) + } + + var onsymlink = function (filename, header) { + xfs.readlink(path.join(cwd, filename), function (err, linkname) { + if (err) return pack.destroy(err) + header.linkname = normalize(linkname) + pack.entry(header, onnextentry) + }) + } + + var onstat = function (err, filename, stat) { + if (err) return pack.destroy(err) + if (!filename) { + if (opts.finalize !== false) pack.finalize() + return finish(pack) + } + + if (stat.isSocket()) return onnextentry() // tar does not support sockets... + + var header = { + name: normalize(filename), + mode: (stat.mode | (stat.isDirectory() ? dmode : fmode)) & umask, + mtime: stat.mtime, + size: stat.size, + type: 'file', + uid: stat.uid, + gid: stat.gid + } + + if (stat.isDirectory()) { + header.size = 0 + header.type = 'directory' + header = map(header) || header + return pack.entry(header, onnextentry) + } + + if (stat.isSymbolicLink()) { + header.size = 0 + header.type = 'symlink' + header = map(header) || header + return onsymlink(filename, header) + } + + // TODO: add fifo etc... + + header = map(header) || header + + if (!stat.isFile()) { + if (strict) return pack.destroy(new Error('unsupported type for ' + filename)) + return onnextentry() + } + + var entry = pack.entry(header, onnextentry) + if (!entry) return + + var rs = mapStream(xfs.createReadStream(path.join(cwd, filename)), header) + + rs.on('error', function (err) { // always forward errors on destroy + entry.destroy(err) + }) + + pump(rs, entry) + } + + var onnextentry = function (err) { + if (err) return pack.destroy(err) + statNext(onstat) + } + + onnextentry() + + return pack +} + +var head = function (list) { + return list.length ? list[list.length - 1] : null +} + +var processGetuid = function () { + return process.getuid ? process.getuid() : -1 +} + +var processUmask = function () { + return process.umask ? process.umask() : 0 +} + +exports.extract = function (cwd, opts) { + if (!cwd) cwd = '.' + if (!opts) opts = {} + + var xfs = opts.fs || fs + var ignore = opts.ignore || opts.filter || noop + var map = opts.map || noop + var mapStream = opts.mapStream || echo + var own = opts.chown !== false && !win32 && processGetuid() === 0 + var extract = opts.extract || tar.extract() + var stack = [] + var now = new Date() + var umask = typeof opts.umask === 'number' ? ~opts.umask : ~processUmask() + var dmode = typeof opts.dmode === 'number' ? opts.dmode : 0 + var fmode = typeof opts.fmode === 'number' ? opts.fmode : 0 + var strict = opts.strict !== false + + if (opts.strip) map = strip(map, opts.strip) + + if (opts.readable) { + dmode |= parseInt(555, 8) + fmode |= parseInt(444, 8) + } + if (opts.writable) { + dmode |= parseInt(333, 8) + fmode |= parseInt(222, 8) + } + + var utimesParent = function (name, cb) { // we just set the mtime on the parent dir again everytime we write an entry + var top + while ((top = head(stack)) && name.slice(0, top[0].length) !== top[0]) stack.pop() + if (!top) return cb() + xfs.utimes(top[0], now, top[1], cb) + } + + var utimes = function (name, header, cb) { + if (opts.utimes === false) return cb() + + if (header.type === 'directory') return xfs.utimes(name, now, header.mtime, cb) + if (header.type === 'symlink') return utimesParent(name, cb) // TODO: how to set mtime on link? + + xfs.utimes(name, now, header.mtime, function (err) { + if (err) return cb(err) + utimesParent(name, cb) + }) + } + + var chperm = function (name, header, cb) { + var link = header.type === 'symlink' + + /* eslint-disable node/no-deprecated-api */ + var chmod = link ? xfs.lchmod : xfs.chmod + var chown = link ? xfs.lchown : xfs.chown + /* eslint-enable node/no-deprecated-api */ + + if (!chmod) return cb() + + var mode = (header.mode | (header.type === 'directory' ? dmode : fmode)) & umask + chmod(name, mode, function (err) { + if (err) return cb(err) + if (!own) return cb() + if (!chown) return cb() + chown(name, header.uid, header.gid, cb) + }) + } + + extract.on('entry', function (header, stream, next) { + header = map(header) || header + header.name = normalize(header.name) + var name = path.join(cwd, path.join('/', header.name)) + + if (ignore(name, header)) { + stream.resume() + return next() + } + + var stat = function (err) { + if (err) return next(err) + utimes(name, header, function (err) { + if (err) return next(err) + if (win32) return next() + chperm(name, header, next) + }) + } + + var onsymlink = function () { + if (win32) return next() // skip symlinks on win for now before it can be tested + xfs.unlink(name, function () { + xfs.symlink(header.linkname, name, stat) + }) + } + + var onlink = function () { + if (win32) return next() // skip links on win for now before it can be tested + xfs.unlink(name, function () { + var srcpath = path.join(cwd, path.join('/', header.linkname)) + + xfs.link(srcpath, name, function (err) { + if (err && err.code === 'EPERM' && opts.hardlinkAsFilesFallback) { + stream = xfs.createReadStream(srcpath) + return onfile() + } + + stat(err) + }) + }) + } + + var onfile = function () { + var ws = xfs.createWriteStream(name) + var rs = mapStream(stream, header) + + ws.on('error', function (err) { // always forward errors on destroy + rs.destroy(err) + }) + + pump(rs, ws, function (err) { + if (err) return next(err) + ws.on('close', stat) + }) + } + + if (header.type === 'directory') { + stack.push([name, header.mtime]) + return mkdirfix(name, { + fs: xfs, own: own, uid: header.uid, gid: header.gid + }, stat) + } + + var dir = path.dirname(name) + + validate(xfs, dir, path.join(cwd, '.'), function (err, valid) { + if (err) return next(err) + if (!valid) return next(new Error(dir + ' is not a valid path')) + + mkdirfix(dir, { + fs: xfs, own: own, uid: header.uid, gid: header.gid + }, function (err) { + if (err) return next(err) + + switch (header.type) { + case 'file': return onfile() + case 'link': return onlink() + case 'symlink': return onsymlink() + } + + if (strict) return next(new Error('unsupported type for ' + name + ' (' + header.type + ')')) + + stream.resume() + next() + }) + }) + }) + + if (opts.finish) extract.on('finish', opts.finish) + + return extract +} + +function validate (fs, name, root, cb) { + if (name === root) return cb(null, true) + fs.lstat(name, function (err, st) { + if (err && err.code !== 'ENOENT') return cb(err) + if (err || st.isDirectory()) return validate(fs, path.join(name, '..'), root, cb) + cb(null, false) + }) +} + +function mkdirfix (name, opts, cb) { + mkdirp(name, { fs: opts.fs }, function (err, made) { + if (!err && made && opts.own) { + chownr(made, opts.uid, opts.gid, cb) + } else { + cb(err) + } + }) +} + + +/***/ }), + +/***/ 129: +/***/ (function(module) { + +module.exports = require("child_process"); + +/***/ }), + +/***/ 139: +/***/ (function(module, __unusedexports, __webpack_require__) { + +module.exports = { + SFTPStream: __webpack_require__(157), + SSH2Stream: __webpack_require__(635), + utils: __webpack_require__(780), + constants: __webpack_require__(801) +}; + +/***/ }), + +/***/ 149: +/***/ (function(module, exports, __webpack_require__) { + +/*! safe-buffer. MIT License. Feross Aboukhadijeh */ +/* eslint-disable node/no-deprecated-api */ +var buffer = __webpack_require__(293) +var Buffer = buffer.Buffer + +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} + +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.prototype = Object.create(Buffer.prototype) + +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) + +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) + } + return buf +} + +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} + +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) +} + + +/***/ }), + +/***/ 154: +/***/ (function(__unusedmodule, exports) { + +var alloc = Buffer.alloc + +var ZEROS = '0000000000000000000' +var SEVENS = '7777777777777777777' +var ZERO_OFFSET = '0'.charCodeAt(0) +var USTAR_MAGIC = Buffer.from('ustar\x00', 'binary') +var USTAR_VER = Buffer.from('00', 'binary') +var GNU_MAGIC = Buffer.from('ustar\x20', 'binary') +var GNU_VER = Buffer.from('\x20\x00', 'binary') +var MASK = parseInt('7777', 8) +var MAGIC_OFFSET = 257 +var VERSION_OFFSET = 263 + +var clamp = function (index, len, defaultValue) { + if (typeof index !== 'number') return defaultValue + index = ~~index // Coerce to integer. + if (index >= len) return len + if (index >= 0) return index + index += len + if (index >= 0) return index + return 0 +} + +var toType = function (flag) { + switch (flag) { + case 0: + return 'file' + case 1: + return 'link' + case 2: + return 'symlink' + case 3: + return 'character-device' + case 4: + return 'block-device' + case 5: + return 'directory' + case 6: + return 'fifo' + case 7: + return 'contiguous-file' + case 72: + return 'pax-header' + case 55: + return 'pax-global-header' + case 27: + return 'gnu-long-link-path' + case 28: + case 30: + return 'gnu-long-path' + } + + return null +} + +var toTypeflag = function (flag) { + switch (flag) { + case 'file': + return 0 + case 'link': + return 1 + case 'symlink': + return 2 + case 'character-device': + return 3 + case 'block-device': + return 4 + case 'directory': + return 5 + case 'fifo': + return 6 + case 'contiguous-file': + return 7 + case 'pax-header': + return 72 + } + + return 0 +} + +var indexOf = function (block, num, offset, end) { + for (; offset < end; offset++) { + if (block[offset] === num) return offset + } + return end +} + +var cksum = function (block) { + var sum = 8 * 32 + for (var i = 0; i < 148; i++) sum += block[i] + for (var j = 156; j < 512; j++) sum += block[j] + return sum +} + +var encodeOct = function (val, n) { + val = val.toString(8) + if (val.length > n) return SEVENS.slice(0, n) + ' ' + else return ZEROS.slice(0, n - val.length) + val + ' ' +} + +/* Copied from the node-tar repo and modified to meet + * tar-stream coding standard. + * + * Source: https://github.com/npm/node-tar/blob/51b6627a1f357d2eb433e7378e5f05e83b7aa6cd/lib/header.js#L349 + */ +function parse256 (buf) { + // first byte MUST be either 80 or FF + // 80 for positive, FF for 2's comp + var positive + if (buf[0] === 0x80) positive = true + else if (buf[0] === 0xFF) positive = false + else return null + + // build up a base-256 tuple from the least sig to the highest + var tuple = [] + for (var i = buf.length - 1; i > 0; i--) { + var byte = buf[i] + if (positive) tuple.push(byte) + else tuple.push(0xFF - byte) + } + + var sum = 0 + var l = tuple.length + for (i = 0; i < l; i++) { + sum += tuple[i] * Math.pow(256, i) + } + + return positive ? sum : -1 * sum +} + +var decodeOct = function (val, offset, length) { + val = val.slice(offset, offset + length) + offset = 0 + + // If prefixed with 0x80 then parse as a base-256 integer + if (val[offset] & 0x80) { + return parse256(val) + } else { + // Older versions of tar can prefix with spaces + while (offset < val.length && val[offset] === 32) offset++ + var end = clamp(indexOf(val, 32, offset, val.length), val.length, val.length) + while (offset < end && val[offset] === 0) offset++ + if (end === offset) return 0 + return parseInt(val.slice(offset, end).toString(), 8) + } +} + +var decodeStr = function (val, offset, length, encoding) { + return val.slice(offset, indexOf(val, 0, offset, offset + length)).toString(encoding) +} + +var addLength = function (str) { + var len = Buffer.byteLength(str) + var digits = Math.floor(Math.log(len) / Math.log(10)) + 1 + if (len + digits >= Math.pow(10, digits)) digits++ + + return (len + digits) + str +} + +exports.decodeLongPath = function (buf, encoding) { + return decodeStr(buf, 0, buf.length, encoding) +} + +exports.encodePax = function (opts) { // TODO: encode more stuff in pax + var result = '' + if (opts.name) result += addLength(' path=' + opts.name + '\n') + if (opts.linkname) result += addLength(' linkpath=' + opts.linkname + '\n') + var pax = opts.pax + if (pax) { + for (var key in pax) { + result += addLength(' ' + key + '=' + pax[key] + '\n') + } + } + return Buffer.from(result) +} + +exports.decodePax = function (buf) { + var result = {} + + while (buf.length) { + var i = 0 + while (i < buf.length && buf[i] !== 32) i++ + var len = parseInt(buf.slice(0, i).toString(), 10) + if (!len) return result + + var b = buf.slice(i + 1, len - 1).toString() + var keyIndex = b.indexOf('=') + if (keyIndex === -1) return result + result[b.slice(0, keyIndex)] = b.slice(keyIndex + 1) + + buf = buf.slice(len) + } + + return result +} + +exports.encode = function (opts) { + var buf = alloc(512) + var name = opts.name + var prefix = '' + + if (opts.typeflag === 5 && name[name.length - 1] !== '/') name += '/' + if (Buffer.byteLength(name) !== name.length) return null // utf-8 + + while (Buffer.byteLength(name) > 100) { + var i = name.indexOf('/') + if (i === -1) return null + prefix += prefix ? '/' + name.slice(0, i) : name.slice(0, i) + name = name.slice(i + 1) + } + + if (Buffer.byteLength(name) > 100 || Buffer.byteLength(prefix) > 155) return null + if (opts.linkname && Buffer.byteLength(opts.linkname) > 100) return null + + buf.write(name) + buf.write(encodeOct(opts.mode & MASK, 6), 100) + buf.write(encodeOct(opts.uid, 6), 108) + buf.write(encodeOct(opts.gid, 6), 116) + buf.write(encodeOct(opts.size, 11), 124) + buf.write(encodeOct((opts.mtime.getTime() / 1000) | 0, 11), 136) + + buf[156] = ZERO_OFFSET + toTypeflag(opts.type) + + if (opts.linkname) buf.write(opts.linkname, 157) + + USTAR_MAGIC.copy(buf, MAGIC_OFFSET) + USTAR_VER.copy(buf, VERSION_OFFSET) + if (opts.uname) buf.write(opts.uname, 265) + if (opts.gname) buf.write(opts.gname, 297) + buf.write(encodeOct(opts.devmajor || 0, 6), 329) + buf.write(encodeOct(opts.devminor || 0, 6), 337) + + if (prefix) buf.write(prefix, 345) + + buf.write(encodeOct(cksum(buf), 6), 148) + + return buf +} + +exports.decode = function (buf, filenameEncoding) { + var typeflag = buf[156] === 0 ? 0 : buf[156] - ZERO_OFFSET + + var name = decodeStr(buf, 0, 100, filenameEncoding) + var mode = decodeOct(buf, 100, 8) + var uid = decodeOct(buf, 108, 8) + var gid = decodeOct(buf, 116, 8) + var size = decodeOct(buf, 124, 12) + var mtime = decodeOct(buf, 136, 12) + var type = toType(typeflag) + var linkname = buf[157] === 0 ? null : decodeStr(buf, 157, 100, filenameEncoding) + var uname = decodeStr(buf, 265, 32) + var gname = decodeStr(buf, 297, 32) + var devmajor = decodeOct(buf, 329, 8) + var devminor = decodeOct(buf, 337, 8) + + var c = cksum(buf) + + // checksum is still initial value if header was null. + if (c === 8 * 32) return null + + // valid checksum + if (c !== decodeOct(buf, 148, 8)) throw new Error('Invalid tar header. Maybe the tar is corrupted or it needs to be gunzipped?') + + if (USTAR_MAGIC.compare(buf, MAGIC_OFFSET, MAGIC_OFFSET + 6) === 0) { + // ustar (posix) format. + // prepend prefix, if present. + if (buf[345]) name = decodeStr(buf, 345, 155, filenameEncoding) + '/' + name + } else if (GNU_MAGIC.compare(buf, MAGIC_OFFSET, MAGIC_OFFSET + 6) === 0 && + GNU_VER.compare(buf, VERSION_OFFSET, VERSION_OFFSET + 2) === 0) { + // 'gnu'/'oldgnu' format. Similar to ustar, but has support for incremental and + // multi-volume tarballs. + } else { + throw new Error('Invalid tar header: unknown format.') + } + + // to support old tar versions that use trailing / to indicate dirs + if (typeflag === 0 && name && name[name.length - 1] === '/') typeflag = 5 + + return { + name, + mode, + uid, + gid, + size, + mtime: new Date(1000 * mtime), + type, + linkname, + uname, + gname, + devmajor, + devminor + } +} + + +/***/ }), + +/***/ 157: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// TODO: support EXTENDED request packets + +var TransformStream = __webpack_require__(413).Transform; +var ReadableStream = __webpack_require__(413).Readable; +var WritableStream = __webpack_require__(413).Writable; +var constants = __webpack_require__(747).constants || process.binding('constants'); +var util = __webpack_require__(669); +var inherits = util.inherits; +var isDate = util.isDate; +var listenerCount = __webpack_require__(614).EventEmitter.listenerCount; +var fs = __webpack_require__(747); + +var readString = __webpack_require__(780).readString; +var readInt = __webpack_require__(780).readInt; +var readUInt32BE = __webpack_require__(523).readUInt32BE; +var writeUInt32BE = __webpack_require__(523).writeUInt32BE; + +var ATTR = { + SIZE: 0x00000001, + UIDGID: 0x00000002, + PERMISSIONS: 0x00000004, + ACMODTIME: 0x00000008, + EXTENDED: 0x80000000 +}; + +var STATUS_CODE = { + OK: 0, + EOF: 1, + NO_SUCH_FILE: 2, + PERMISSION_DENIED: 3, + FAILURE: 4, + BAD_MESSAGE: 5, + NO_CONNECTION: 6, + CONNECTION_LOST: 7, + OP_UNSUPPORTED: 8 +}; +Object.keys(STATUS_CODE).forEach(function(key) { + STATUS_CODE[STATUS_CODE[key]] = key; +}); +var STATUS_CODE_STR = { + 0: 'No error', + 1: 'End of file', + 2: 'No such file or directory', + 3: 'Permission denied', + 4: 'Failure', + 5: 'Bad message', + 6: 'No connection', + 7: 'Connection lost', + 8: 'Operation unsupported' +}; +SFTPStream.STATUS_CODE = STATUS_CODE; + +var REQUEST = { + INIT: 1, + OPEN: 3, + CLOSE: 4, + READ: 5, + WRITE: 6, + LSTAT: 7, + FSTAT: 8, + SETSTAT: 9, + FSETSTAT: 10, + OPENDIR: 11, + READDIR: 12, + REMOVE: 13, + MKDIR: 14, + RMDIR: 15, + REALPATH: 16, + STAT: 17, + RENAME: 18, + READLINK: 19, + SYMLINK: 20, + EXTENDED: 200 +}; +Object.keys(REQUEST).forEach(function(key) { + REQUEST[REQUEST[key]] = key; +}); + +var RESPONSE = { + VERSION: 2, + STATUS: 101, + HANDLE: 102, + DATA: 103, + NAME: 104, + ATTRS: 105, + EXTENDED: 201 +}; +Object.keys(RESPONSE).forEach(function(key) { + RESPONSE[RESPONSE[key]] = key; +}); + +var OPEN_MODE = { + READ: 0x00000001, + WRITE: 0x00000002, + APPEND: 0x00000004, + CREAT: 0x00000008, + TRUNC: 0x00000010, + EXCL: 0x00000020 +}; +SFTPStream.OPEN_MODE = OPEN_MODE; + +var MAX_PKT_LEN = 34000; +var MAX_REQID = Math.pow(2, 32) - 1; +var CLIENT_VERSION_BUFFER = Buffer.from([0, 0, 0, 5 /* length */, + REQUEST.INIT, + 0, 0, 0, 3 /* version */]); +var SERVER_VERSION_BUFFER = Buffer.from([0, 0, 0, 5 /* length */, + RESPONSE.VERSION, + 0, 0, 0, 3 /* version */]); +/* + http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02: + + The maximum size of a packet is in practice determined by the client + (the maximum size of read or write requests that it sends, plus a few + bytes of packet overhead). All servers SHOULD support packets of at + least 34000 bytes (where the packet size refers to the full length, + including the header above). This should allow for reads and writes + of at most 32768 bytes. + + OpenSSH caps this to 256kb instead of the ~34kb as mentioned in the sftpv3 + spec. +*/ +var RE_OPENSSH = /^SSH-2.0-(?:OpenSSH|dropbear)/; +var OPENSSH_MAX_DATA_LEN = (256 * 1024) - (2 * 1024)/*account for header data*/; + +function DEBUG_NOOP(msg) {} + +function SFTPStream(cfg, remoteIdentRaw) { + if (typeof cfg === 'string' && !remoteIdentRaw) { + remoteIdentRaw = cfg; + cfg = undefined; + } + if (typeof cfg !== 'object' || !cfg) + cfg = {}; + + TransformStream.call(this, { + highWaterMark: (typeof cfg.highWaterMark === 'number' + ? cfg.highWaterMark + : 32 * 1024) + }); + + this.debug = (typeof cfg.debug === 'function' ? cfg.debug : DEBUG_NOOP); + this.server = (cfg.server ? true : false); + this._isOpenSSH = (remoteIdentRaw && RE_OPENSSH.test(remoteIdentRaw)); + this._needContinue = false; + this._state = { + // common + status: 'packet_header', + writeReqid: -1, + pktLeft: undefined, + pktHdrBuf: Buffer.allocUnsafe(9), // room for pktLen + pktType + req id + pktBuf: undefined, + pktType: undefined, + version: undefined, + extensions: {}, + + // client + maxDataLen: (this._isOpenSSH ? OPENSSH_MAX_DATA_LEN : 32768), + requests: {} + }; + + var self = this; + this.on('end', function() { + self.readable = false; + }).on('finish', onFinish) + .on('prefinish', onFinish); + function onFinish() { + self.writable = false; + self._cleanup(false); + } + + if (!this.server) + this.push(CLIENT_VERSION_BUFFER); +} +inherits(SFTPStream, TransformStream); + +SFTPStream.prototype.__read = TransformStream.prototype._read; +SFTPStream.prototype._read = function(n) { + if (this._needContinue) { + this._needContinue = false; + this.emit('continue'); + } + return this.__read(n); +}; +SFTPStream.prototype.__push = TransformStream.prototype.push; +SFTPStream.prototype.push = function(chunk, encoding) { + if (!this.readable) + return false; + if (chunk === null) + this.readable = false; + var ret = this.__push(chunk, encoding); + this._needContinue = (ret === false); + return ret; +}; + +SFTPStream.prototype._cleanup = function(callback) { + var state = this._state; + + state.pktBuf = undefined; // give GC something to do + + var requests = state.requests; + var keys = Object.keys(requests); + var len = keys.length; + if (len) { + if (this.readable) { + var err = new Error('SFTP session ended early'); + for (var i = 0, cb; i < len; ++i) + (cb = requests[keys[i]].cb) && cb(err); + } + state.requests = {}; + } + + if (this.readable) + this.push(null); + if (!this._readableState.endEmitted && !this._readableState.flowing) { + // Ugh! + this.resume(); + } + if (callback !== false) { + this.debug('DEBUG[SFTP]: Parser: Malformed packet'); + callback && callback(new Error('Malformed packet')); + } +}; + +SFTPStream.prototype._transform = function(chunk, encoding, callback) { + var state = this._state; + var server = this.server; + var status = state.status; + var pktType = state.pktType; + var pktBuf = state.pktBuf; + var pktLeft = state.pktLeft; + var version = state.version; + var pktHdrBuf = state.pktHdrBuf; + var requests = state.requests; + var debug = this.debug; + var chunkLen = chunk.length; + var chunkPos = 0; + var buffer; + var chunkLeft; + var id; + + while (true) { + if (status === 'discard') { + chunkLeft = (chunkLen - chunkPos); + if (pktLeft <= chunkLeft) { + chunkPos += pktLeft; + pktLeft = 0; + status = 'packet_header'; + buffer = pktBuf = undefined; + } else { + pktLeft -= chunkLeft; + break; + } + } else if (pktBuf !== undefined) { + chunkLeft = (chunkLen - chunkPos); + if (pktLeft <= chunkLeft) { + chunk.copy(pktBuf, + pktBuf.length - pktLeft, + chunkPos, + chunkPos + pktLeft); + chunkPos += pktLeft; + pktLeft = 0; + buffer = pktBuf; + pktBuf = undefined; + continue; + } else { + chunk.copy(pktBuf, pktBuf.length - pktLeft, chunkPos); + pktLeft -= chunkLeft; + break; + } + } else if (status === 'packet_header') { + if (!buffer) { + pktLeft = 5; + pktBuf = pktHdrBuf; + } else { + // here we read the right-most 5 bytes from buffer (pktHdrBuf) + pktLeft = readUInt32BE(buffer, 4) - 1; // account for type byte + pktType = buffer[8]; + + if (server) { + if (version === undefined && pktType !== REQUEST.INIT) { + debug('DEBUG[SFTP]: Parser: Unexpected packet before init'); + this._cleanup(false); + return callback(new Error('Unexpected packet before init')); + } else if (version !== undefined && pktType === REQUEST.INIT) { + debug('DEBUG[SFTP]: Parser: Unexpected duplicate init'); + status = 'bad_pkt'; + } else if (pktLeft > MAX_PKT_LEN) { + var msg = 'Packet length (' + + pktLeft + + ') exceeds max length (' + + MAX_PKT_LEN + + ')'; + debug('DEBUG[SFTP]: Parser: ' + msg); + this._cleanup(false); + return callback(new Error(msg)); + } else if (pktType === REQUEST.EXTENDED) { + status = 'bad_pkt'; + } else if (REQUEST[pktType] === undefined) { + debug('DEBUG[SFTP]: Parser: Unsupported packet type: ' + pktType); + status = 'discard'; + } + } else if (version === undefined && pktType !== RESPONSE.VERSION) { + debug('DEBUG[SFTP]: Parser: Unexpected packet before version'); + this._cleanup(false); + return callback(new Error('Unexpected packet before version')); + } else if (version !== undefined && pktType === RESPONSE.VERSION) { + debug('DEBUG[SFTP]: Parser: Unexpected duplicate version'); + status = 'bad_pkt'; + } else if (RESPONSE[pktType] === undefined) { + status = 'discard'; + } + + if (status === 'bad_pkt') { + // Copy original packet info to left of pktHdrBuf + writeUInt32BE(pktHdrBuf, pktLeft + 1, 0); + pktHdrBuf[4] = pktType; + + pktLeft = 4; + pktBuf = pktHdrBuf; + } else { + pktBuf = Buffer.allocUnsafe(pktLeft); + status = 'payload'; + } + } + } else if (status === 'payload') { + if (pktType === RESPONSE.VERSION || pktType === REQUEST.INIT) { + /* + uint32 version + + */ + version = state.version = readInt(buffer, 0, this, callback); + if (version === false) + return; + if (version < 3) { + this._cleanup(false); + return callback(new Error('Incompatible SFTP version: ' + version)); + } else if (server) + this.push(SERVER_VERSION_BUFFER); + + var buflen = buffer.length; + var extname; + var extdata; + buffer._pos = 4; + while (buffer._pos < buflen) { + extname = readString(buffer, buffer._pos, 'ascii', this, callback); + if (extname === false) + return; + extdata = readString(buffer, buffer._pos, 'ascii', this, callback); + if (extdata === false) + return; + if (state.extensions[extname]) + state.extensions[extname].push(extdata); + else + state.extensions[extname] = [ extdata ]; + } + + this.emit('ready'); + } else { + /* + All other packets (client and server) begin with a (client) request + id: + uint32 id + */ + id = readInt(buffer, 0, this, callback); + if (id === false) + return; + + var filename; + var attrs; + var handle; + var data; + + if (!server) { + var req = requests[id]; + var cb = req && req.cb; + debug('DEBUG[SFTP]: Parser: Response: ' + RESPONSE[pktType]); + if (req && cb) { + if (pktType === RESPONSE.STATUS) { + /* + uint32 error/status code + string error message (ISO-10646 UTF-8) + string language tag + */ + var code = readInt(buffer, 4, this, callback); + if (code === false) + return; + if (code === STATUS_CODE.OK) { + cb(); + } else { + // We borrow OpenSSH behavior here, specifically we make the + // message and language fields optional, despite the + // specification requiring them (even if they are empty). This + // helps to avoid problems with buggy implementations that do + // not fully conform to the SFTP(v3) specification. + var msg; + var lang = ''; + if (buffer.length >= 12) { + msg = readString(buffer, 8, 'utf8', this, callback); + if (msg === false) + return; + if ((buffer._pos + 4) < buffer.length) { + lang = readString(buffer, + buffer._pos, + 'ascii', + this, + callback); + if (lang === false) + return; + } + } + var err = new Error(msg + || STATUS_CODE_STR[code] + || 'Unknown status'); + err.code = code; + err.lang = lang; + cb(err); + } + } else if (pktType === RESPONSE.HANDLE) { + /* + string handle + */ + handle = readString(buffer, 4, this, callback); + if (handle === false) + return; + cb(undefined, handle); + } else if (pktType === RESPONSE.DATA) { + /* + string data + */ + if (req.buffer) { + // we have already pre-allocated space to store the data + var dataLen = readInt(buffer, 4, this, callback); + if (dataLen === false) + return; + var reqBufLen = req.buffer.length; + if (dataLen > reqBufLen) { + // truncate response data to fit expected size + writeUInt32BE(buffer, reqBufLen, 4); + } + data = readString(buffer, 4, req.buffer, this, callback); + if (data === false) + return; + cb(undefined, data, dataLen); + } else { + data = readString(buffer, 4, this, callback); + if (data === false) + return; + cb(undefined, data); + } + } else if (pktType === RESPONSE.NAME) { + /* + uint32 count + repeats count times: + string filename + string longname + ATTRS attrs + */ + var namesLen = readInt(buffer, 4, this, callback); + if (namesLen === false) + return; + var names = [], + longname; + buffer._pos = 8; + for (var i = 0; i < namesLen; ++i) { + // we are going to assume UTF-8 for filenames despite the SFTPv3 + // spec not specifying an encoding because the specs for newer + // versions of the protocol all explicitly specify UTF-8 for + // filenames + filename = readString(buffer, + buffer._pos, + 'utf8', + this, + callback); + if (filename === false) + return; + // `longname` only exists in SFTPv3 and since it typically will + // contain the filename, we assume it is also UTF-8 + longname = readString(buffer, + buffer._pos, + 'utf8', + this, + callback); + if (longname === false) + return; + attrs = readAttrs(buffer, buffer._pos, this, callback); + if (attrs === false) + return; + names.push({ + filename: filename, + longname: longname, + attrs: attrs + }); + } + cb(undefined, names); + } else if (pktType === RESPONSE.ATTRS) { + /* + ATTRS attrs + */ + attrs = readAttrs(buffer, 4, this, callback); + if (attrs === false) + return; + cb(undefined, attrs); + } else if (pktType === RESPONSE.EXTENDED) { + if (req.extended) { + switch (req.extended) { + case 'statvfs@openssh.com': + case 'fstatvfs@openssh.com': + /* + uint64 f_bsize // file system block size + uint64 f_frsize // fundamental fs block size + uint64 f_blocks // number of blocks (unit f_frsize) + uint64 f_bfree // free blocks in file system + uint64 f_bavail // free blocks for non-root + uint64 f_files // total file inodes + uint64 f_ffree // free file inodes + uint64 f_favail // free file inodes for to non-root + uint64 f_fsid // file system id + uint64 f_flag // bit mask of f_flag values + uint64 f_namemax // maximum filename length + */ + var stats = { + f_bsize: undefined, + f_frsize: undefined, + f_blocks: undefined, + f_bfree: undefined, + f_bavail: undefined, + f_files: undefined, + f_ffree: undefined, + f_favail: undefined, + f_sid: undefined, + f_flag: undefined, + f_namemax: undefined + }; + stats.f_bsize = readUInt64BE(buffer, 4, this, callback); + if (stats.f_bsize === false) + return; + stats.f_frsize = readUInt64BE(buffer, 12, this, callback); + if (stats.f_frsize === false) + return; + stats.f_blocks = readUInt64BE(buffer, 20, this, callback); + if (stats.f_blocks === false) + return; + stats.f_bfree = readUInt64BE(buffer, 28, this, callback); + if (stats.f_bfree === false) + return; + stats.f_bavail = readUInt64BE(buffer, 36, this, callback); + if (stats.f_bavail === false) + return; + stats.f_files = readUInt64BE(buffer, 44, this, callback); + if (stats.f_files === false) + return; + stats.f_ffree = readUInt64BE(buffer, 52, this, callback); + if (stats.f_ffree === false) + return; + stats.f_favail = readUInt64BE(buffer, 60, this, callback); + if (stats.f_favail === false) + return; + stats.f_sid = readUInt64BE(buffer, 68, this, callback); + if (stats.f_sid === false) + return; + stats.f_flag = readUInt64BE(buffer, 76, this, callback); + if (stats.f_flag === false) + return; + stats.f_namemax = readUInt64BE(buffer, 84, this, callback); + if (stats.f_namemax === false) + return; + cb(undefined, stats); + break; + } + } + // XXX: at least provide the raw buffer data to the callback in + // case of unexpected extended response? + cb(); + } + } + if (req) + delete requests[id]; + } else { + // server + var evName = REQUEST[pktType]; + var offset; + var path; + + debug('DEBUG[SFTP]: Parser: Request: ' + evName); + if (listenerCount(this, evName)) { + if (pktType === REQUEST.OPEN) { + /* + string filename + uint32 pflags + ATTRS attrs + */ + filename = readString(buffer, 4, 'utf8', this, callback); + if (filename === false) + return; + var pflags = readInt(buffer, buffer._pos, this, callback); + if (pflags === false) + return; + attrs = readAttrs(buffer, buffer._pos + 4, this, callback); + if (attrs === false) + return; + this.emit(evName, id, filename, pflags, attrs); + } else if (pktType === REQUEST.CLOSE + || pktType === REQUEST.FSTAT + || pktType === REQUEST.READDIR) { + /* + string handle + */ + handle = readString(buffer, 4, this, callback); + if (handle === false) + return; + this.emit(evName, id, handle); + } else if (pktType === REQUEST.READ) { + /* + string handle + uint64 offset + uint32 len + */ + handle = readString(buffer, 4, this, callback); + if (handle === false) + return; + offset = readUInt64BE(buffer, buffer._pos, this, callback); + if (offset === false) + return; + var len = readInt(buffer, buffer._pos, this, callback); + if (len === false) + return; + this.emit(evName, id, handle, offset, len); + } else if (pktType === REQUEST.WRITE) { + /* + string handle + uint64 offset + string data + */ + handle = readString(buffer, 4, this, callback); + if (handle === false) + return; + offset = readUInt64BE(buffer, buffer._pos, this, callback); + if (offset === false) + return; + data = readString(buffer, buffer._pos, this, callback); + if (data === false) + return; + this.emit(evName, id, handle, offset, data); + } else if (pktType === REQUEST.LSTAT + || pktType === REQUEST.STAT + || pktType === REQUEST.OPENDIR + || pktType === REQUEST.REMOVE + || pktType === REQUEST.RMDIR + || pktType === REQUEST.REALPATH + || pktType === REQUEST.READLINK) { + /* + string path + */ + path = readString(buffer, 4, 'utf8', this, callback); + if (path === false) + return; + this.emit(evName, id, path); + } else if (pktType === REQUEST.SETSTAT + || pktType === REQUEST.MKDIR) { + /* + string path + ATTRS attrs + */ + path = readString(buffer, 4, 'utf8', this, callback); + if (path === false) + return; + attrs = readAttrs(buffer, buffer._pos, this, callback); + if (attrs === false) + return; + this.emit(evName, id, path, attrs); + } else if (pktType === REQUEST.FSETSTAT) { + /* + string handle + ATTRS attrs + */ + handle = readString(buffer, 4, this, callback); + if (handle === false) + return; + attrs = readAttrs(buffer, buffer._pos, this, callback); + if (attrs === false) + return; + this.emit(evName, id, handle, attrs); + } else if (pktType === REQUEST.RENAME + || pktType === REQUEST.SYMLINK) { + /* + RENAME: + string oldpath + string newpath + SYMLINK: + string linkpath + string targetpath + */ + var str1; + var str2; + str1 = readString(buffer, 4, 'utf8', this, callback); + if (str1 === false) + return; + str2 = readString(buffer, buffer._pos, 'utf8', this, callback); + if (str2 === false) + return; + if (pktType === REQUEST.SYMLINK && this._isOpenSSH) { + // OpenSSH has linkpath and targetpath positions switched + this.emit(evName, id, str2, str1); + } else + this.emit(evName, id, str1, str2); + } + } else { + // automatically reject request if no handler for request type + this.status(id, STATUS_CODE.OP_UNSUPPORTED); + } + } + } + + // prepare for next packet + status = 'packet_header'; + buffer = pktBuf = undefined; + } else if (status === 'bad_pkt') { + if (server && buffer[4] !== REQUEST.INIT) { + var errCode = (buffer[4] === REQUEST.EXTENDED + ? STATUS_CODE.OP_UNSUPPORTED + : STATUS_CODE.FAILURE); + + // no request id for init/version packets, so we have no way to send a + // status response, so we just close up shop ... + if (buffer[4] === REQUEST.INIT || buffer[4] === RESPONSE.VERSION) + return this._cleanup(callback); + + id = readInt(buffer, 5, this, callback); + if (id === false) + return; + this.status(id, errCode); + } + + // by this point we have already read the type byte and the id bytes, so + // we subtract those from the number of bytes to skip + pktLeft = readUInt32BE(buffer, 0) - 5; + + status = 'discard'; + } + + if (chunkPos >= chunkLen) + break; + } + + state.status = status; + state.pktType = pktType; + state.pktBuf = pktBuf; + state.pktLeft = pktLeft; + state.version = version; + + callback(); +}; + +// client +SFTPStream.prototype.createReadStream = function(path, options) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + return new ReadStream(this, path, options); +}; +SFTPStream.prototype.createWriteStream = function(path, options) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + return new WriteStream(this, path, options); +}; +SFTPStream.prototype.open = function(path, flags_, attrs, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + if (typeof attrs === 'function') { + cb = attrs; + attrs = undefined; + } + + var flags = (typeof flags_ === 'number' ? flags_ : stringToFlags(flags_)); + if (flags === null) + throw new Error('Unknown flags string: ' + flags_); + + var attrFlags = 0; + var attrBytes = 0; + if (typeof attrs === 'string' || typeof attrs === 'number') { + attrs = { mode: attrs }; + } + if (typeof attrs === 'object' && attrs !== null) { + attrs = attrsToBytes(attrs); + attrFlags = attrs.flags; + attrBytes = attrs.nbytes; + attrs = attrs.bytes; + } + + /* + uint32 id + string filename + uint32 pflags + ATTRS attrs + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathlen + 4 + 4 + attrBytes); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.OPEN; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, pathlen, p); + buf.write(path, p += 4, pathlen, 'utf8'); + writeUInt32BE(buf, flags, p += pathlen); + writeUInt32BE(buf, attrFlags, p += 4); + if (attrs && attrFlags) { + p += 4; + for (var i = 0, len = attrs.length; i < len; ++i) + for (var j = 0, len2 = attrs[i].length; j < len2; ++j) + buf[p++] = attrs[i][j]; + } + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing OPEN'); + return this.push(buf); +}; +SFTPStream.prototype.close = function(handle, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!Buffer.isBuffer(handle)) + throw new Error('handle is not a Buffer'); + + var state = this._state; + + /* + uint32 id + string handle + */ + var handlelen = handle.length; + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + handlelen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.CLOSE; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, handlelen, p); + handle.copy(buf, p += 4); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing CLOSE'); + return this.push(buf); +}; +SFTPStream.prototype.readData = function(handle, buf, off, len, position, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!Buffer.isBuffer(handle)) + throw new Error('handle is not a Buffer'); + else if (!Buffer.isBuffer(buf)) + throw new Error('buffer is not a Buffer'); + else if (off >= buf.length) + throw new Error('offset is out of bounds'); + else if (off + len > buf.length) + throw new Error('length extends beyond buffer'); + else if (position === null) + throw new Error('null position currently unsupported'); + + var state = this._state; + + /* + uint32 id + string handle + uint64 offset + uint32 len + */ + var handlelen = handle.length; + var p = 9; + var pos = position; + var out = Buffer.allocUnsafe(4 + 1 + 4 + 4 + handlelen + 8 + 4); + + writeUInt32BE(out, out.length - 4, 0); + out[4] = REQUEST.READ; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(out, reqid, 5); + + writeUInt32BE(out, handlelen, p); + handle.copy(out, p += 4); + p += handlelen; + for (var i = 7; i >= 0; --i) { + out[p + i] = pos & 0xFF; + pos /= 256; + } + writeUInt32BE(out, len, p += 8); + + state.requests[reqid] = { + cb: function(err, data, nb) { + if (err) { + if (cb._wantEOFError || err.code !== STATUS_CODE.EOF) + return cb(err); + } else if (nb > len) { + return cb(new Error('Received more data than requested')); + } + cb(undefined, nb || 0, data, position); + }, + buffer: buf.slice(off, off + len) + }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing READ'); + return this.push(out); +}; +SFTPStream.prototype.writeData = function(handle, buf, off, len, position, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!Buffer.isBuffer(handle)) + throw new Error('handle is not a Buffer'); + else if (!Buffer.isBuffer(buf)) + throw new Error('buffer is not a Buffer'); + else if (off > buf.length) + throw new Error('offset is out of bounds'); + else if (off + len > buf.length) + throw new Error('length extends beyond buffer'); + else if (position === null) + throw new Error('null position currently unsupported'); + + var self = this; + var state = this._state; + + if (!len) { + cb && process.nextTick(function() { cb(undefined, 0); }); + return; + } + + var overflow = (len > state.maxDataLen + ? len - state.maxDataLen + : 0); + var origPosition = position; + + if (overflow) + len = state.maxDataLen; + + /* + uint32 id + string handle + uint64 offset + string data + */ + var handlelen = handle.length; + var p = 9; + var out = Buffer.allocUnsafe(4 + 1 + 4 + 4 + handlelen + 8 + 4 + len); + + writeUInt32BE(out, out.length - 4, 0); + out[4] = REQUEST.WRITE; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(out, reqid, 5); + + writeUInt32BE(out, handlelen, p); + handle.copy(out, p += 4); + p += handlelen; + for (var i = 7; i >= 0; --i) { + out[p + i] = position & 0xFF; + position /= 256; + } + writeUInt32BE(out, len, p += 8); + buf.copy(out, p += 4, off, off + len); + + state.requests[reqid] = { + cb: function(err) { + if (err) + cb && cb(err); + else if (overflow) { + self.writeData(handle, + buf, + off + len, + overflow, + origPosition + len, + cb); + } else + cb && cb(undefined, off + len); + } + }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing WRITE'); + return this.push(out); +}; +function tryCreateBuffer(size) { + try { + return Buffer.allocUnsafe(size); + } catch (ex) { + return ex; + } +} +function fastXfer(src, dst, srcPath, dstPath, opts, cb) { + var concurrency = 64; + var chunkSize = 32768; + //var preserve = false; + var onstep; + var mode; + var fileSize; + + if (typeof opts === 'function') { + cb = opts; + } else if (typeof opts === 'object' && opts !== null) { + if (typeof opts.concurrency === 'number' + && opts.concurrency > 0 + && !isNaN(opts.concurrency)) + concurrency = opts.concurrency; + if (typeof opts.chunkSize === 'number' + && opts.chunkSize > 0 + && !isNaN(opts.chunkSize)) + chunkSize = opts.chunkSize; + if (typeof opts.fileSize === 'number' + && opts.fileSize > 0 + && !isNaN(opts.fileSize)) + fileSize = opts.fileSize; + if (typeof opts.step === 'function') + onstep = opts.step; + //preserve = (opts.preserve ? true : false); + if (typeof opts.mode === 'string' || typeof opts.mode === 'number') + mode = modeNum(opts.mode); + } + + // internal state variables + var fsize; + var pdst = 0; + var total = 0; + var hadError = false; + var srcHandle; + var dstHandle; + var readbuf; + var bufsize = chunkSize * concurrency; + + function onerror(err) { + if (hadError) + return; + + hadError = true; + + var left = 0; + var cbfinal; + + if (srcHandle || dstHandle) { + cbfinal = function() { + if (--left === 0) + cb(err); + }; + if (srcHandle && (src === fs || src.writable)) + ++left; + if (dstHandle && (dst === fs || dst.writable)) + ++left; + if (srcHandle && (src === fs || src.writable)) + src.close(srcHandle, cbfinal); + if (dstHandle && (dst === fs || dst.writable)) + dst.close(dstHandle, cbfinal); + } else + cb(err); + } + + src.open(srcPath, 'r', function(err, sourceHandle) { + if (err) + return onerror(err); + + srcHandle = sourceHandle; + + if (fileSize === undefined) + src.fstat(srcHandle, tryStat); + else + tryStat(null, { size: fileSize }); + + function tryStat(err, attrs) { + if (err) { + if (src !== fs) { + // Try stat() for sftp servers that may not support fstat() for + // whatever reason + src.stat(srcPath, function(err_, attrs_) { + if (err_) + return onerror(err); + tryStat(null, attrs_); + }); + return; + } + return onerror(err); + } + fsize = attrs.size; + + dst.open(dstPath, 'w', function(err, destHandle) { + if (err) + return onerror(err); + + dstHandle = destHandle; + + if (fsize <= 0) + return onerror(); + + // Use less memory where possible + while (bufsize > fsize) { + if (concurrency === 1) { + bufsize = fsize; + break; + } + bufsize -= chunkSize; + --concurrency; + } + + readbuf = tryCreateBuffer(bufsize); + if (readbuf instanceof Error) + return onerror(readbuf); + + if (mode !== undefined) { + dst.fchmod(dstHandle, mode, function tryAgain(err) { + if (err) { + // Try chmod() for sftp servers that may not support fchmod() for + // whatever reason + dst.chmod(dstPath, mode, function(err_) { + tryAgain(); + }); + return; + } + startReads(); + }); + } else { + startReads(); + } + + function onread(err, nb, data, dstpos, datapos, origChunkLen) { + if (err) + return onerror(err); + + datapos = datapos || 0; + + if (src === fs) + dst.writeData(dstHandle, readbuf, datapos, nb, dstpos, writeCb); + else + dst.write(dstHandle, readbuf, datapos, nb, dstpos, writeCb); + + function writeCb(err) { + if (err) + return onerror(err); + + total += nb; + onstep && onstep(total, nb, fsize); + + if (nb < origChunkLen) + return singleRead(datapos, dstpos + nb, origChunkLen - nb); + + if (total === fsize) { + dst.close(dstHandle, function(err) { + dstHandle = undefined; + if (err) + return onerror(err); + src.close(srcHandle, function(err) { + srcHandle = undefined; + if (err) + return onerror(err); + cb(); + }); + }); + return; + } + + if (pdst >= fsize) + return; + + var chunk = (pdst + chunkSize > fsize ? fsize - pdst : chunkSize); + singleRead(datapos, pdst, chunk); + pdst += chunk; + } + } + + function makeCb(psrc, pdst, chunk) { + return function(err, nb, data) { + onread(err, nb, data, pdst, psrc, chunk); + }; + } + + function singleRead(psrc, pdst, chunk) { + if (src === fs) { + src.read(srcHandle, + readbuf, + psrc, + chunk, + pdst, + makeCb(psrc, pdst, chunk)); + } else { + src.readData(srcHandle, + readbuf, + psrc, + chunk, + pdst, + makeCb(psrc, pdst, chunk)); + } + } + + function startReads() { + var reads = 0; + var psrc = 0; + while (pdst < fsize && reads < concurrency) { + var chunk = (pdst + chunkSize > fsize ? fsize - pdst : chunkSize); + singleRead(psrc, pdst, chunk); + psrc += chunk; + pdst += chunk; + ++reads; + } + } + }); + } + }); +} +SFTPStream.prototype.fastGet = function(remotePath, localPath, opts, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + fastXfer(this, fs, remotePath, localPath, opts, cb); +}; +SFTPStream.prototype.fastPut = function(localPath, remotePath, opts, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + fastXfer(fs, this, localPath, remotePath, opts, cb); +}; +SFTPStream.prototype.readFile = function(path, options, callback_) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var callback; + if (typeof callback_ === 'function') { + callback = callback_; + } else if (typeof options === 'function') { + callback = options; + options = undefined; + } + + var self = this; + + if (typeof options === 'string') + options = { encoding: options, flag: 'r' }; + else if (!options) + options = { encoding: null, flag: 'r' }; + else if (typeof options !== 'object') + throw new TypeError('Bad arguments'); + + var encoding = options.encoding; + if (encoding && !Buffer.isEncoding(encoding)) + throw new Error('Unknown encoding: ' + encoding); + + // first, stat the file, so we know the size. + var size; + var buffer; // single buffer with file data + var buffers; // list for when size is unknown + var pos = 0; + var handle; + + // SFTPv3 does not support using -1 for read position, so we have to track + // read position manually + var bytesRead = 0; + + var flag = options.flag || 'r'; + this.open(path, flag, 438 /*=0666*/, function(er, handle_) { + if (er) + return callback && callback(er); + handle = handle_; + + self.fstat(handle, function tryStat(er, st) { + if (er) { + // Try stat() for sftp servers that may not support fstat() for + // whatever reason + self.stat(path, function(er_, st_) { + if (er_) { + return self.close(handle, function() { + callback && callback(er); + }); + } + tryStat(null, st_); + }); + return; + } + + size = st.size || 0; + if (size === 0) { + // the kernel lies about many files. + // Go ahead and try to read some bytes. + buffers = []; + return read(); + } + + buffer = Buffer.allocUnsafe(size); + read(); + }); + }); + + function read() { + if (size === 0) { + buffer = Buffer.allocUnsafe(8192); + self.readData(handle, buffer, 0, 8192, bytesRead, afterRead); + } else { + self.readData(handle, buffer, pos, size - pos, bytesRead, afterRead); + } + } + + function afterRead(er, nbytes) { + var eof; + if (er) { + eof = (er.code === STATUS_CODE.EOF); + if (!eof) { + return self.close(handle, function() { + return callback && callback(er); + }); + } + } else { + eof = false; + } + + if (eof || (size === 0 && nbytes === 0)) + return close(); + + bytesRead += nbytes; + pos += nbytes; + if (size !== 0) { + if (pos === size) + close(); + else + read(); + } else { + // unknown size, just read until we don't get bytes. + buffers.push(buffer.slice(0, nbytes)); + read(); + } + } + afterRead._wantEOFError = true; + + function close() { + self.close(handle, function(er) { + if (size === 0) { + // collected the data into the buffers list. + buffer = Buffer.concat(buffers, pos); + } else if (pos < size) { + buffer = buffer.slice(0, pos); + } + + if (encoding) + buffer = buffer.toString(encoding); + return callback && callback(er, buffer); + }); + } +}; +function writeAll(self, handle, buffer, offset, length, position, callback_) { + var callback = (typeof callback_ === 'function' ? callback_ : undefined); + + self.writeData(handle, + buffer, + offset, + length, + position, + function(writeErr, written) { + if (writeErr) { + return self.close(handle, function() { + callback && callback(writeErr); + }); + } + if (written === length) + self.close(handle, callback); + else { + offset += written; + length -= written; + position += written; + writeAll(self, handle, buffer, offset, length, position, callback); + } + }); +} +SFTPStream.prototype.writeFile = function(path, data, options, callback_) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var callback; + if (typeof callback_ === 'function') { + callback = callback_; + } else if (typeof options === 'function') { + callback = options; + options = undefined; + } + var self = this; + + if (typeof options === 'string') + options = { encoding: options, mode: 438, flag: 'w' }; + else if (!options) + options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' }; + else if (typeof options !== 'object') + throw new TypeError('Bad arguments'); + + if (options.encoding && !Buffer.isEncoding(options.encoding)) + throw new Error('Unknown encoding: ' + options.encoding); + + var flag = options.flag || 'w'; + this.open(path, flag, options.mode, function(openErr, handle) { + if (openErr) + callback && callback(openErr); + else { + var buffer = (Buffer.isBuffer(data) + ? data + : Buffer.from('' + data, options.encoding || 'utf8')); + var position = (/a/.test(flag) ? null : 0); + + // SFTPv3 does not support the notion of 'current position' + // (null position), so we just attempt to append to the end of the file + // instead + if (position === null) { + self.fstat(handle, function tryStat(er, st) { + if (er) { + // Try stat() for sftp servers that may not support fstat() for + // whatever reason + self.stat(path, function(er_, st_) { + if (er_) { + return self.close(handle, function() { + callback && callback(er); + }); + } + tryStat(null, st_); + }); + return; + } + writeAll(self, handle, buffer, 0, buffer.length, st.size, callback); + }); + return; + } + writeAll(self, handle, buffer, 0, buffer.length, position, callback); + } + }); +}; +SFTPStream.prototype.appendFile = function(path, data, options, callback_) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var callback; + if (typeof callback_ === 'function') { + callback = callback_; + } else if (typeof options === 'function') { + callback = options; + options = undefined; + } + + if (typeof options === 'string') + options = { encoding: options, mode: 438, flag: 'a' }; + else if (!options) + options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' }; + else if (typeof options !== 'object') + throw new TypeError('Bad arguments'); + + if (!options.flag) + options = util._extend({ flag: 'a' }, options); + this.writeFile(path, data, options, callback); +}; +SFTPStream.prototype.exists = function(path, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + this.stat(path, function(err) { + cb && cb(err ? false : true); + }); +}; +SFTPStream.prototype.unlink = function(filename, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + /* + uint32 id + string filename + */ + var fnamelen = Buffer.byteLength(filename); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + fnamelen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.REMOVE; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, fnamelen, p); + buf.write(filename, p += 4, fnamelen, 'utf8'); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing REMOVE'); + return this.push(buf); +}; +SFTPStream.prototype.rename = function(oldPath, newPath, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + /* + uint32 id + string oldpath + string newpath + */ + var oldlen = Buffer.byteLength(oldPath); + var newlen = Buffer.byteLength(newPath); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + oldlen + 4 + newlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.RENAME; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, oldlen, p); + buf.write(oldPath, p += 4, oldlen, 'utf8'); + writeUInt32BE(buf, newlen, p += oldlen); + buf.write(newPath, p += 4, newlen, 'utf8'); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing RENAME'); + return this.push(buf); +}; +SFTPStream.prototype.mkdir = function(path, attrs, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var flags = 0; + var attrBytes = 0; + var state = this._state; + + if (typeof attrs === 'function') { + cb = attrs; + attrs = undefined; + } + if (typeof attrs === 'object' && attrs !== null) { + attrs = attrsToBytes(attrs); + flags = attrs.flags; + attrBytes = attrs.nbytes; + attrs = attrs.bytes; + } + + /* + uint32 id + string path + ATTRS attrs + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathlen + 4 + attrBytes); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.MKDIR; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, pathlen, p); + buf.write(path, p += 4, pathlen, 'utf8'); + writeUInt32BE(buf, flags, p += pathlen); + if (flags) { + p += 4; + for (var i = 0, len = attrs.length; i < len; ++i) + for (var j = 0, len2 = attrs[i].length; j < len2; ++j) + buf[p++] = attrs[i][j]; + } + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing MKDIR'); + return this.push(buf); +}; +SFTPStream.prototype.rmdir = function(path, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + /* + uint32 id + string path + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.RMDIR; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, pathlen, p); + buf.write(path, p += 4, pathlen, 'utf8'); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing RMDIR'); + return this.push(buf); +}; +SFTPStream.prototype.readdir = function(where, opts, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + var doFilter; + + if (typeof opts === 'function') { + cb = opts; + opts = {}; + } + if (typeof opts !== 'object' || opts === null) + opts = {}; + + doFilter = (opts && opts.full ? false : true); + + if (!Buffer.isBuffer(where) && typeof where !== 'string') + throw new Error('missing directory handle or path'); + + if (typeof where === 'string') { + var self = this; + var entries = []; + var e = 0; + + return this.opendir(where, function reread(err, handle) { + if (err) + return cb(err); + + self.readdir(handle, opts, function(err, list) { + var eof = (err && err.code === STATUS_CODE.EOF); + + if (err && !eof) { + return self.close(handle, function() { + cb(err); + }); + } else if (eof) { + return self.close(handle, function(err) { + if (err) + return cb(err); + cb(undefined, entries); + }); + } + + for (var i = 0, len = list.length; i < len; ++i, ++e) + entries[e] = list[i]; + + reread(undefined, handle); + }); + }); + } + + /* + uint32 id + string handle + */ + var handlelen = where.length; + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + handlelen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.READDIR; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, handlelen, p); + where.copy(buf, p += 4); + + state.requests[reqid] = { + cb: (doFilter + ? function(err, list) { + if (err) + return cb(err); + + for (var i = list.length - 1; i >= 0; --i) { + if (list[i].filename === '.' || list[i].filename === '..') + list.splice(i, 1); + } + + cb(undefined, list); + } + : cb) + }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing READDIR'); + return this.push(buf); +}; +SFTPStream.prototype.fstat = function(handle, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!Buffer.isBuffer(handle)) + throw new Error('handle is not a Buffer'); + + var state = this._state; + + /* + uint32 id + string handle + */ + var handlelen = handle.length; + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + handlelen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.FSTAT; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, handlelen, p); + handle.copy(buf, p += 4); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing FSTAT'); + return this.push(buf); +}; +SFTPStream.prototype.stat = function(path, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + /* + uint32 id + string path + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.STAT; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, pathlen, p); + buf.write(path, p += 4, pathlen, 'utf8'); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing STAT'); + return this.push(buf); +}; +SFTPStream.prototype.lstat = function(path, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + /* + uint32 id + string path + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.LSTAT; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, pathlen, p); + buf.write(path, p += 4, pathlen, 'utf8'); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing LSTAT'); + return this.push(buf); +}; +SFTPStream.prototype.opendir = function(path, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + /* + uint32 id + string path + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.OPENDIR; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, pathlen, p); + buf.write(path, p += 4, pathlen, 'utf8'); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing OPENDIR'); + return this.push(buf); +}; +SFTPStream.prototype.setstat = function(path, attrs, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var flags = 0; + var attrBytes = 0; + var state = this._state; + + if (typeof attrs === 'object' && attrs !== null) { + attrs = attrsToBytes(attrs); + flags = attrs.flags; + attrBytes = attrs.nbytes; + attrs = attrs.bytes; + } else if (typeof attrs === 'function') + cb = attrs; + + /* + uint32 id + string path + ATTRS attrs + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathlen + 4 + attrBytes); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.SETSTAT; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, pathlen, p); + buf.write(path, p += 4, pathlen, 'utf8'); + writeUInt32BE(buf, flags, p += pathlen); + if (flags) { + p += 4; + for (var i = 0, len = attrs.length; i < len; ++i) + for (var j = 0, len2 = attrs[i].length; j < len2; ++j) + buf[p++] = attrs[i][j]; + } + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing SETSTAT'); + return this.push(buf); +}; +SFTPStream.prototype.fsetstat = function(handle, attrs, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!Buffer.isBuffer(handle)) + throw new Error('handle is not a Buffer'); + + var flags = 0; + var attrBytes = 0; + var state = this._state; + + if (typeof attrs === 'object' && attrs !== null) { + attrs = attrsToBytes(attrs); + flags = attrs.flags; + attrBytes = attrs.nbytes; + attrs = attrs.bytes; + } else if (typeof attrs === 'function') + cb = attrs; + + /* + uint32 id + string handle + ATTRS attrs + */ + var handlelen = handle.length; + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + handlelen + 4 + attrBytes); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.FSETSTAT; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, handlelen, p); + handle.copy(buf, p += 4); + writeUInt32BE(buf, flags, p += handlelen); + if (flags) { + p += 4; + for (var i = 0, len = attrs.length; i < len; ++i) + for (var j = 0, len2 = attrs[i].length; j < len2; ++j) + buf[p++] = attrs[i][j]; + } + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing FSETSTAT'); + return this.push(buf); +}; +SFTPStream.prototype.futimes = function(handle, atime, mtime, cb) { + return this.fsetstat(handle, { + atime: toUnixTimestamp(atime), + mtime: toUnixTimestamp(mtime) + }, cb); +}; +SFTPStream.prototype.utimes = function(path, atime, mtime, cb) { + return this.setstat(path, { + atime: toUnixTimestamp(atime), + mtime: toUnixTimestamp(mtime) + }, cb); +}; +SFTPStream.prototype.fchown = function(handle, uid, gid, cb) { + return this.fsetstat(handle, { + uid: uid, + gid: gid + }, cb); +}; +SFTPStream.prototype.chown = function(path, uid, gid, cb) { + return this.setstat(path, { + uid: uid, + gid: gid + }, cb); +}; +SFTPStream.prototype.fchmod = function(handle, mode, cb) { + return this.fsetstat(handle, { + mode: mode + }, cb); +}; +SFTPStream.prototype.chmod = function(path, mode, cb) { + return this.setstat(path, { + mode: mode + }, cb); +}; +SFTPStream.prototype.readlink = function(path, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + /* + uint32 id + string path + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.READLINK; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, pathlen, p); + buf.write(path, p += 4, pathlen, 'utf8'); + + state.requests[reqid] = { + cb: function(err, names) { + if (err) + return cb(err); + else if (!names || !names.length) + return cb(new Error('Response missing link info')); + cb(undefined, names[0].filename); + } + }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing READLINK'); + return this.push(buf); +}; +SFTPStream.prototype.symlink = function(targetPath, linkPath, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + /* + uint32 id + string linkpath + string targetpath + */ + var linklen = Buffer.byteLength(linkPath); + var targetlen = Buffer.byteLength(targetPath); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + linklen + 4 + targetlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.SYMLINK; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + if (this._isOpenSSH) { + // OpenSSH has linkpath and targetpath positions switched + writeUInt32BE(buf, targetlen, p); + buf.write(targetPath, p += 4, targetlen, 'utf8'); + writeUInt32BE(buf, linklen, p += targetlen); + buf.write(linkPath, p += 4, linklen, 'utf8'); + } else { + writeUInt32BE(buf, linklen, p); + buf.write(linkPath, p += 4, linklen, 'utf8'); + writeUInt32BE(buf, targetlen, p += linklen); + buf.write(targetPath, p += 4, targetlen, 'utf8'); + } + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing SYMLINK'); + return this.push(buf); +}; +SFTPStream.prototype.realpath = function(path, cb) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var state = this._state; + + /* + uint32 id + string path + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.REALPATH; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + + writeUInt32BE(buf, pathlen, p); + buf.write(path, p += 4, pathlen, 'utf8'); + + state.requests[reqid] = { + cb: function(err, names) { + if (err) + return cb(err); + else if (!names || !names.length) + return cb(new Error('Response missing path info')); + cb(undefined, names[0].filename); + } + }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing REALPATH'); + return this.push(buf); +}; +// extended requests +SFTPStream.prototype.ext_openssh_rename = function(oldPath, newPath, cb) { + var state = this._state; + + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!state.extensions['posix-rename@openssh.com'] + || state.extensions['posix-rename@openssh.com'].indexOf('1') === -1) + throw new Error('Server does not support this extended request'); + + /* + uint32 id + string "posix-rename@openssh.com" + string oldpath + string newpath + */ + var oldlen = Buffer.byteLength(oldPath); + var newlen = Buffer.byteLength(newPath); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 24 + 4 + oldlen + 4 + newlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.EXTENDED; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + writeUInt32BE(buf, 24, p); + buf.write('posix-rename@openssh.com', p += 4, 24, 'ascii'); + + writeUInt32BE(buf, oldlen, p += 24); + buf.write(oldPath, p += 4, oldlen, 'utf8'); + writeUInt32BE(buf, newlen, p += oldlen); + buf.write(newPath, p += 4, newlen, 'utf8'); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing posix-rename@openssh.com'); + return this.push(buf); +}; +SFTPStream.prototype.ext_openssh_statvfs = function(path, cb) { + var state = this._state; + + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!state.extensions['statvfs@openssh.com'] + || state.extensions['statvfs@openssh.com'].indexOf('2') === -1) + throw new Error('Server does not support this extended request'); + + /* + uint32 id + string "statvfs@openssh.com" + string path + */ + var pathlen = Buffer.byteLength(path); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 19 + 4 + pathlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.EXTENDED; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + writeUInt32BE(buf, 19, p); + buf.write('statvfs@openssh.com', p += 4, 19, 'ascii'); + + writeUInt32BE(buf, pathlen, p += 19); + buf.write(path, p += 4, pathlen, 'utf8'); + + state.requests[reqid] = { + extended: 'statvfs@openssh.com', + cb: cb + }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing statvfs@openssh.com'); + return this.push(buf); +}; +SFTPStream.prototype.ext_openssh_fstatvfs = function(handle, cb) { + var state = this._state; + + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!state.extensions['fstatvfs@openssh.com'] + || state.extensions['fstatvfs@openssh.com'].indexOf('2') === -1) + throw new Error('Server does not support this extended request'); + else if (!Buffer.isBuffer(handle)) + throw new Error('handle is not a Buffer'); + + /* + uint32 id + string "fstatvfs@openssh.com" + string handle + */ + var handlelen = handle.length; + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 20 + 4 + handlelen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.EXTENDED; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + writeUInt32BE(buf, 20, p); + buf.write('fstatvfs@openssh.com', p += 4, 20, 'ascii'); + + writeUInt32BE(buf, handlelen, p += 20); + buf.write(handle, p += 4, handlelen, 'utf8'); + + state.requests[reqid] = { + extended: 'fstatvfs@openssh.com', + cb: cb + }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing fstatvfs@openssh.com'); + return this.push(buf); +}; +SFTPStream.prototype.ext_openssh_hardlink = function(oldPath, newPath, cb) { + var state = this._state; + + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!state.extensions['hardlink@openssh.com'] + || state.extensions['hardlink@openssh.com'].indexOf('1') === -1) + throw new Error('Server does not support this extended request'); + + /* + uint32 id + string "hardlink@openssh.com" + string oldpath + string newpath + */ + var oldlen = Buffer.byteLength(oldPath); + var newlen = Buffer.byteLength(newPath); + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 20 + 4 + oldlen + 4 + newlen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.EXTENDED; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + writeUInt32BE(buf, 20, p); + buf.write('hardlink@openssh.com', p += 4, 20, 'ascii'); + + writeUInt32BE(buf, oldlen, p += 20); + buf.write(oldPath, p += 4, oldlen, 'utf8'); + writeUInt32BE(buf, newlen, p += oldlen); + buf.write(newPath, p += 4, newlen, 'utf8'); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing hardlink@openssh.com'); + return this.push(buf); +}; +SFTPStream.prototype.ext_openssh_fsync = function(handle, cb) { + var state = this._state; + + if (this.server) + throw new Error('Client-only method called in server mode'); + else if (!state.extensions['fsync@openssh.com'] + || state.extensions['fsync@openssh.com'].indexOf('1') === -1) + throw new Error('Server does not support this extended request'); + else if (!Buffer.isBuffer(handle)) + throw new Error('handle is not a Buffer'); + + /* + uint32 id + string "fsync@openssh.com" + string handle + */ + var handlelen = handle.length; + var p = 9; + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 17 + 4 + handlelen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = REQUEST.EXTENDED; + var reqid = state.writeReqid = (state.writeReqid + 1) % MAX_REQID; + writeUInt32BE(buf, reqid, 5); + writeUInt32BE(buf, 17, p); + buf.write('fsync@openssh.com', p += 4, 17, 'ascii'); + + writeUInt32BE(buf, handlelen, p += 17); + buf.write(handle, p += 4, handlelen, 'utf8'); + + state.requests[reqid] = { cb: cb }; + + this.debug('DEBUG[SFTP]: Outgoing: Writing fsync@openssh.com'); + return this.push(buf); +}; + +// server +SFTPStream.prototype.status = function(id, code, message, lang) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + if (!STATUS_CODE[code] || typeof code !== 'number') + throw new Error('Bad status code: ' + code); + + message || (message = ''); + lang || (lang = ''); + + var msgLen = Buffer.byteLength(message); + var langLen = Buffer.byteLength(lang); + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 4 + msgLen + 4 + langLen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = RESPONSE.STATUS; + writeUInt32BE(buf, id, 5); + + writeUInt32BE(buf, code, 9); + + writeUInt32BE(buf, msgLen, 13); + if (msgLen) + buf.write(message, 17, msgLen, 'utf8'); + + writeUInt32BE(buf, langLen, 17 + msgLen); + if (langLen) + buf.write(lang, 17 + msgLen + 4, langLen, 'ascii'); + + this.debug('DEBUG[SFTP]: Outgoing: Writing STATUS'); + return this.push(buf); +}; +SFTPStream.prototype.handle = function(id, handle) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + if (!Buffer.isBuffer(handle)) + throw new Error('handle is not a Buffer'); + + var handleLen = handle.length; + + if (handleLen > 256) + throw new Error('handle too large (> 256 bytes)'); + + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + handleLen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = RESPONSE.HANDLE; + writeUInt32BE(buf, id, 5); + + writeUInt32BE(buf, handleLen, 9); + if (handleLen) + handle.copy(buf, 13); + + this.debug('DEBUG[SFTP]: Outgoing: Writing HANDLE'); + return this.push(buf); +}; +SFTPStream.prototype.data = function(id, data, encoding) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var isBuffer = Buffer.isBuffer(data); + + if (!isBuffer && typeof data !== 'string') + throw new Error('data is not a Buffer or string'); + + if (!isBuffer) + encoding || (encoding = 'utf8'); + + var dataLen = (isBuffer ? data.length : Buffer.byteLength(data, encoding)); + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + dataLen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = RESPONSE.DATA; + writeUInt32BE(buf, id, 5); + + writeUInt32BE(buf, dataLen, 9); + if (dataLen) { + if (isBuffer) + data.copy(buf, 13); + else + buf.write(data, 13, dataLen, encoding); + } + + this.debug('DEBUG[SFTP]: Outgoing: Writing DATA'); + return this.push(buf); +}; +SFTPStream.prototype.name = function(id, names) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + if (!Array.isArray(names)) { + if (typeof names !== 'object' || names === null) + throw new Error('names is not an object or array'); + names = [ names ]; + } + + var count = names.length; + var namesLen = 0; + var nameAttrs; + var attrs = []; + var name; + var filename; + var longname; + var attr; + var len; + var len2; + var buf; + var p; + var i; + var j; + var k; + + for (i = 0; i < count; ++i) { + name = names[i]; + filename = (!name || !name.filename || typeof name.filename !== 'string' + ? '' + : name.filename); + namesLen += 4 + Buffer.byteLength(filename); + longname = (!name || !name.longname || typeof name.longname !== 'string' + ? '' + : name.longname); + namesLen += 4 + Buffer.byteLength(longname); + + if (typeof name.attrs === 'object' && name.attrs !== null) { + nameAttrs = attrsToBytes(name.attrs); + namesLen += 4 + nameAttrs.nbytes; + attrs.push(nameAttrs); + } else { + namesLen += 4; + attrs.push(null); + } + } + + buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + namesLen); + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = RESPONSE.NAME; + writeUInt32BE(buf, id, 5); + + writeUInt32BE(buf, count, 9); + + p = 13; + + for (i = 0; i < count; ++i) { + name = names[i]; + + filename = (!name || !name.filename || typeof name.filename !== 'string' + ? '' + : name.filename); + len = Buffer.byteLength(filename); + writeUInt32BE(buf, len, p); + p += 4; + if (len) { + buf.write(filename, p, len, 'utf8'); + p += len; + } + + longname = (!name || !name.longname || typeof name.longname !== 'string' + ? '' + : name.longname); + len = Buffer.byteLength(longname); + writeUInt32BE(buf, len, p); + p += 4; + if (len) { + buf.write(longname, p, len, 'utf8'); + p += len; + } + + attr = attrs[i]; + if (attr) { + writeUInt32BE(buf, attr.flags, p); + p += 4; + if (attr.flags && attr.bytes) { + var bytes = attr.bytes; + for (j = 0, len = bytes.length; j < len; ++j) + for (k = 0, len2 = bytes[j].length; k < len2; ++k) + buf[p++] = bytes[j][k]; + } + } else { + writeUInt32BE(buf, 0, p); + p += 4; + } + } + + this.debug('DEBUG[SFTP]: Outgoing: Writing NAME'); + return this.push(buf); +}; +SFTPStream.prototype.attrs = function(id, attrs) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + if (typeof attrs !== 'object' || attrs === null) + throw new Error('attrs is not an object'); + + var info = attrsToBytes(attrs); + var buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + info.nbytes); + var p = 13; + + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = RESPONSE.ATTRS; + writeUInt32BE(buf, id, 5); + + writeUInt32BE(buf, info.flags, 9); + + if (info.flags && info.bytes) { + var bytes = info.bytes; + for (var j = 0, len = bytes.length; j < len; ++j) + for (var k = 0, len2 = bytes[j].length; k < len2; ++k) + buf[p++] = bytes[j][k]; + } + + this.debug('DEBUG[SFTP]: Outgoing: Writing ATTRS'); + return this.push(buf); +}; + +function readAttrs(buf, p, stream, callback) { + /* + uint32 flags + uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE + uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID + uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID + uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS + uint32 atime present only if flag SSH_FILEXFER_ACMODTIME + uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME + uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED + string extended_type + string extended_data + ... more extended data (extended_type - extended_data pairs), + so that number of pairs equals extended_count + */ + var flags = readUInt32BE(buf, p); + var attrs = new Stats(); + + p += 4; + + if (flags & ATTR.SIZE) { + var size = readUInt64BE(buf, p, stream, callback); + if (size === false) + return false; + attrs.size = size; + p += 8; + } + if (flags & ATTR.UIDGID) { + var uid; + var gid; + uid = readInt(buf, p, this, callback); + if (uid === false) + return false; + attrs.uid = uid; + p += 4; + gid = readInt(buf, p, this, callback); + if (gid === false) + return false; + attrs.gid = gid; + p += 4; + } + if (flags & ATTR.PERMISSIONS) { + var mode = readInt(buf, p, this, callback); + if (mode === false) + return false; + attrs.mode = mode; + // backwards compatibility + attrs.permissions = mode; + p += 4; + } + if (flags & ATTR.ACMODTIME) { + var atime; + var mtime; + atime = readInt(buf, p, this, callback); + if (atime === false) + return false; + attrs.atime = atime; + p += 4; + mtime = readInt(buf, p, this, callback); + if (mtime === false) + return false; + attrs.mtime = mtime; + p += 4; + } + if (flags & ATTR.EXTENDED) { + // TODO: read/parse extended data + var extcount = readInt(buf, p, this, callback); + if (extcount === false) + return false; + p += 4; + for (var i = 0, len; i < extcount; ++i) { + len = readInt(buf, p, this, callback); + if (len === false) + return false; + p += 4 + len; + } + } + + buf._pos = p; + + return attrs; +} + +function readUInt64BE(buffer, p, stream, callback) { + if ((buffer.length - p) < 8) { + stream && stream._cleanup(callback); + return false; + } + + var val = 0; + + for (var len = p + 8; p < len; ++p) { + val *= 256; + val += buffer[p]; + } + + buffer._pos = p; + + return val; +} + +function attrsToBytes(attrs) { + var flags = 0; + var attrBytes = 0; + var ret = []; + var i = 0; + + if (typeof attrs !== 'object' || attrs === null) + return { flags: flags, nbytes: attrBytes, bytes: ret }; + + if (typeof attrs.size === 'number') { + flags |= ATTR.SIZE; + attrBytes += 8; + var sizeBytes = new Array(8); + var val = attrs.size; + for (i = 7; i >= 0; --i) { + sizeBytes[i] = val & 0xFF; + val /= 256; + } + ret.push(sizeBytes); + } + if (typeof attrs.uid === 'number' && typeof attrs.gid === 'number') { + flags |= ATTR.UIDGID; + attrBytes += 8; + ret.push([(attrs.uid >> 24) & 0xFF, (attrs.uid >> 16) & 0xFF, + (attrs.uid >> 8) & 0xFF, attrs.uid & 0xFF]); + ret.push([(attrs.gid >> 24) & 0xFF, (attrs.gid >> 16) & 0xFF, + (attrs.gid >> 8) & 0xFF, attrs.gid & 0xFF]); + } + if (typeof attrs.permissions === 'number' + || typeof attrs.permissions === 'string' + || typeof attrs.mode === 'number' + || typeof attrs.mode === 'string') { + var mode = modeNum(attrs.mode || attrs.permissions); + flags |= ATTR.PERMISSIONS; + attrBytes += 4; + ret.push([(mode >> 24) & 0xFF, + (mode >> 16) & 0xFF, + (mode >> 8) & 0xFF, + mode & 0xFF]); + } + if ((typeof attrs.atime === 'number' || isDate(attrs.atime)) + && (typeof attrs.mtime === 'number' || isDate(attrs.mtime))) { + var atime = toUnixTimestamp(attrs.atime); + var mtime = toUnixTimestamp(attrs.mtime); + + flags |= ATTR.ACMODTIME; + attrBytes += 8; + ret.push([(atime >> 24) & 0xFF, (atime >> 16) & 0xFF, + (atime >> 8) & 0xFF, atime & 0xFF]); + ret.push([(mtime >> 24) & 0xFF, (mtime >> 16) & 0xFF, + (mtime >> 8) & 0xFF, mtime & 0xFF]); + } + // TODO: extended attributes + + return { flags: flags, nbytes: attrBytes, bytes: ret }; +} + +function toUnixTimestamp(time) { + if (typeof time === 'number' && !isNaN(time)) + return time; + else if (isDate(time)) + return parseInt(time.getTime() / 1000, 10); + throw new Error('Cannot parse time: ' + time); +} + +function modeNum(mode) { + if (typeof mode === 'number' && !isNaN(mode)) + return mode; + else if (typeof mode === 'string') + return modeNum(parseInt(mode, 8)); + throw new Error('Cannot parse mode: ' + mode); +} + +var stringFlagMap = { + 'r': OPEN_MODE.READ, + 'r+': OPEN_MODE.READ | OPEN_MODE.WRITE, + 'w': OPEN_MODE.TRUNC | OPEN_MODE.CREAT | OPEN_MODE.WRITE, + 'wx': OPEN_MODE.TRUNC | OPEN_MODE.CREAT | OPEN_MODE.WRITE | OPEN_MODE.EXCL, + 'xw': OPEN_MODE.TRUNC | OPEN_MODE.CREAT | OPEN_MODE.WRITE | OPEN_MODE.EXCL, + 'w+': OPEN_MODE.TRUNC | OPEN_MODE.CREAT | OPEN_MODE.READ | OPEN_MODE.WRITE, + 'wx+': OPEN_MODE.TRUNC | OPEN_MODE.CREAT | OPEN_MODE.READ | OPEN_MODE.WRITE + | OPEN_MODE.EXCL, + 'xw+': OPEN_MODE.TRUNC | OPEN_MODE.CREAT | OPEN_MODE.READ | OPEN_MODE.WRITE + | OPEN_MODE.EXCL, + 'a': OPEN_MODE.APPEND | OPEN_MODE.CREAT | OPEN_MODE.WRITE, + 'ax': OPEN_MODE.APPEND | OPEN_MODE.CREAT | OPEN_MODE.WRITE | OPEN_MODE.EXCL, + 'xa': OPEN_MODE.APPEND | OPEN_MODE.CREAT | OPEN_MODE.WRITE | OPEN_MODE.EXCL, + 'a+': OPEN_MODE.APPEND | OPEN_MODE.CREAT | OPEN_MODE.READ | OPEN_MODE.WRITE, + 'ax+': OPEN_MODE.APPEND | OPEN_MODE.CREAT | OPEN_MODE.READ | OPEN_MODE.WRITE + | OPEN_MODE.EXCL, + 'xa+': OPEN_MODE.APPEND | OPEN_MODE.CREAT | OPEN_MODE.READ | OPEN_MODE.WRITE + | OPEN_MODE.EXCL +}; +var stringFlagMapKeys = Object.keys(stringFlagMap); + +function stringToFlags(str) { + var flags = stringFlagMap[str]; + if (flags !== undefined) + return flags; + return null; +} +SFTPStream.stringToFlags = stringToFlags; + +function flagsToString(flags) { + for (var i = 0; i < stringFlagMapKeys.length; ++i) { + var key = stringFlagMapKeys[i]; + if (stringFlagMap[key] === flags) + return key; + } + return null; +} +SFTPStream.flagsToString = flagsToString; + +function Stats(initial) { + this.mode = (initial && initial.mode); + this.permissions = this.mode; // backwards compatiblity + this.uid = (initial && initial.uid); + this.gid = (initial && initial.gid); + this.size = (initial && initial.size); + this.atime = (initial && initial.atime); + this.mtime = (initial && initial.mtime); +} +Stats.prototype._checkModeProperty = function(property) { + return ((this.mode & constants.S_IFMT) === property); +}; +Stats.prototype.isDirectory = function() { + return this._checkModeProperty(constants.S_IFDIR); +}; +Stats.prototype.isFile = function() { + return this._checkModeProperty(constants.S_IFREG); +}; +Stats.prototype.isBlockDevice = function() { + return this._checkModeProperty(constants.S_IFBLK); +}; +Stats.prototype.isCharacterDevice = function() { + return this._checkModeProperty(constants.S_IFCHR); +}; +Stats.prototype.isSymbolicLink = function() { + return this._checkModeProperty(constants.S_IFLNK); +}; +Stats.prototype.isFIFO = function() { + return this._checkModeProperty(constants.S_IFIFO); +}; +Stats.prototype.isSocket = function() { + return this._checkModeProperty(constants.S_IFSOCK); +}; +SFTPStream.Stats = Stats; + +// ============================================================================= +// ReadStream/WriteStream-related +var fsCompat = __webpack_require__(599); +var validateNumber = fsCompat.validateNumber; +var destroyImpl = fsCompat.destroyImpl; +var ERR_OUT_OF_RANGE = fsCompat.ERR_OUT_OF_RANGE; +var ERR_INVALID_ARG_TYPE = fsCompat.ERR_INVALID_ARG_TYPE; + +var kMinPoolSpace = 128; + +var pool; +// It can happen that we expect to read a large chunk of data, and reserve +// a large chunk of the pool accordingly, but the read() call only filled +// a portion of it. If a concurrently executing read() then uses the same pool, +// the "reserved" portion cannot be used, so we allow it to be re-used as a +// new pool later. +var poolFragments = []; + +function allocNewPool(poolSize) { + if (poolFragments.length > 0) + pool = poolFragments.pop(); + else + pool = Buffer.allocUnsafe(poolSize); + pool.used = 0; +} + +// Check the `this.start` and `this.end` of stream. +function checkPosition(pos, name) { + if (!Number.isSafeInteger(pos)) { + validateNumber(pos, name); + if (!Number.isInteger(pos)) + throw new ERR_OUT_OF_RANGE(name, 'an integer', pos); + throw new ERR_OUT_OF_RANGE(name, '>= 0 and <= 2 ** 53 - 1', pos); + } + if (pos < 0) + throw new ERR_OUT_OF_RANGE(name, '>= 0 and <= 2 ** 53 - 1', pos); +} + +function roundUpToMultipleOf8(n) { + return (n + 7) & ~7; // Align to 8 byte boundary. +} + +function ReadStream(sftp, path, options) { + if (options === undefined) + options = {}; + else if (typeof options === 'string') + options = { encoding: options }; + else if (options === null || typeof options !== 'object') + throw new TypeError('"options" argument must be a string or an object'); + else + options = Object.create(options); + + // A little bit bigger buffer and water marks by default + if (options.highWaterMark === undefined) + options.highWaterMark = 64 * 1024; + + // For backwards compat do not emit close on destroy. + options.emitClose = false; + + ReadableStream.call(this, options); + + this.path = path; + this.flags = options.flags === undefined ? 'r' : options.flags; + this.mode = options.mode === undefined ? 0o666 : options.mode; + + this.start = options.start; + this.end = options.end; + this.autoClose = options.autoClose === undefined ? true : options.autoClose; + this.pos = 0; + this.bytesRead = 0; + this.closed = false; + + this.handle = options.handle === undefined ? null : options.handle; + this.sftp = sftp; + this._opening = false; + + if (this.start !== undefined) { + checkPosition(this.start, 'start'); + + this.pos = this.start; + } + + if (this.end === undefined) { + this.end = Infinity; + } else if (this.end !== Infinity) { + checkPosition(this.end, 'end'); + + if (this.start !== undefined && this.start > this.end) { + throw new ERR_OUT_OF_RANGE( + 'start', + `<= "end" (here: ${this.end})`, + this.start + ); + } + } + + this.on('end', function() { + if (this.autoClose) + this.destroy(); + }); + + if (!Buffer.isBuffer(this.handle)) + this.open(); +} +inherits(ReadStream, ReadableStream); + +ReadStream.prototype.open = function() { + if (this._opening) + return; + + this._opening = true; + + this.sftp.open(this.path, this.flags, this.mode, (er, handle) => { + this._opening = false; + + if (er) { + this.emit('error', er); + if (this.autoClose) + this.destroy(); + return; + } + + this.handle = handle; + this.emit('open', handle); + this.emit('ready'); + // start the flow of data. + this.read(); + }); +}; + +ReadStream.prototype._read = function(n) { + if (!Buffer.isBuffer(this.handle)) { + return this.once('open', function() { + this._read(n); + }); + } + + // XXX: safe to remove this? + if (this.destroyed) + return; + + if (!pool || pool.length - pool.used < kMinPoolSpace) { + // discard the old pool. + allocNewPool(this.readableHighWaterMark + || this._readableState.highWaterMark); + } + + // Grab another reference to the pool in the case that while we're + // in the thread pool another read() finishes up the pool, and + // allocates a new one. + var thisPool = pool; + var toRead = Math.min(pool.length - pool.used, n); + var start = pool.used; + + if (this.end !== undefined) + toRead = Math.min(this.end - this.pos + 1, toRead); + + // Already read everything we were supposed to read! + // treat as EOF. + if (toRead <= 0) + return this.push(null); + + // the actual read. + this.sftp.readData(this.handle, + pool, + pool.used, + toRead, + this.pos, + (er, bytesRead) => { + if (er) { + this.emit('error', er); + if (this.autoClose) + this.destroy(); + return; + } + var b = null; + + // Now that we know how much data we have actually read, re-wind the + // 'used' field if we can, and otherwise allow the remainder of our + // reservation to be used as a new pool later. + if (start + toRead === thisPool.used && thisPool === pool) { + var newUsed = thisPool.used + bytesRead - toRead; + thisPool.used = roundUpToMultipleOf8(newUsed); + } else { + // Round down to the next lowest multiple of 8 to ensure the new pool + // fragment start and end positions are aligned to an 8 byte boundary. + var alignedEnd = (start + toRead) & ~7; + var alignedStart = roundUpToMultipleOf8(start + bytesRead); + if (alignedEnd - alignedStart >= kMinPoolSpace) + poolFragments.push(thisPool.slice(alignedStart, alignedEnd)); + } + + if (bytesRead > 0) { + this.bytesRead += bytesRead; + b = thisPool.slice(start, start + bytesRead); + } + + // Move the pool positions, and internal position for reading. + this.pos += bytesRead; + + this.push(b); + }); + + pool.used = roundUpToMultipleOf8(pool.used + toRead); +}; + +if (typeof ReadableStream.prototype.destroy !== 'function') + ReadStream.prototype.destroy = destroyImpl; + +ReadStream.prototype._destroy = function(err, cb) { + if (this._opening && !Buffer.isBuffer(this.handle)) { + this.once('open', closeStream.bind(null, this, cb, err)); + return; + } + + closeStream(this, cb, err); + this.handle = null; + this._opening = false; +}; + +function closeStream(stream, cb, err) { + if (!stream.handle) + return onclose(); + + stream.sftp.close(stream.handle, onclose); + + function onclose(er) { + er = er || err; + cb(er); + stream.closed = true; + if (!er) + stream.emit('close'); + } +} + +ReadStream.prototype.close = function(cb) { + this.destroy(null, cb); +}; + +Object.defineProperty(ReadStream.prototype, 'pending', { + get() { return this.handle === null; }, + configurable: true +}); + +function WriteStream(sftp, path, options) { + if (options === undefined) + options = {}; + else if (typeof options === 'string') + options = { encoding: options }; + else if (options === null || typeof options !== 'object') + throw new TypeError('"options" argument must be a string or an object'); + else + options = Object.create(options); + + // For backwards compat do not emit close on destroy. + options.emitClose = false; + + WritableStream.call(this, options); + + this.path = path; + this.flags = options.flags === undefined ? 'w' : options.flags; + this.mode = options.mode === undefined ? 0o666 : options.mode; + + this.start = options.start; + this.autoClose = options.autoClose === undefined ? true : options.autoClose; + this.pos = 0; + this.bytesWritten = 0; + this.closed = false; + + this.handle = options.handle === undefined ? null : options.handle; + this.sftp = sftp; + this._opening = false; + + if (this.start !== undefined) { + checkPosition(this.start, 'start'); + + this.pos = this.start; + } + + if (options.encoding) + this.setDefaultEncoding(options.encoding); + + // Node v6.x only + this.on('finish', function() { + if (this._writableState.finalCalled) + return; + if (this.autoClose) + this.destroy(); + }); + + if (!Buffer.isBuffer(this.handle)) + this.open(); +} +inherits(WriteStream, WritableStream); + +WriteStream.prototype._final = function(cb) { + if (this.autoClose) + this.destroy(); + cb(); +}; + +WriteStream.prototype.open = function() { + if (this._opening) + return; + + this._opening = true; + + this.sftp.open(this.path, this.flags, this.mode, (er, handle) => { + this._opening = false; + + if (er) { + this.emit('error', er); + if (this.autoClose) + this.destroy(); + return; + } + + this.handle = handle; + + var tryAgain = (err) => { + if (err) { + // Try chmod() for sftp servers that may not support fchmod() for + // whatever reason + this.sftp.chmod(this.path, this.mode, (err_) => { + tryAgain(); + }); + return; + } + + // SFTPv3 requires absolute offsets, no matter the open flag used + if (this.flags[0] === 'a') { + var tryStat = (err, st) => { + if (err) { + // Try stat() for sftp servers that may not support fstat() for + // whatever reason + this.sftp.stat(this.path, (err_, st_) => { + if (err_) { + this.destroy(); + this.emit('error', err); + return; + } + tryStat(null, st_); + }); + return; + } + + this.pos = st.size; + this.emit('open', handle); + this.emit('ready'); + }; + + this.sftp.fstat(handle, tryStat); + return; + } + + this.emit('open', handle); + this.emit('ready'); + }; + + this.sftp.fchmod(handle, this.mode, tryAgain); + }); +}; + +WriteStream.prototype._write = function(data, encoding, cb) { + if (!Buffer.isBuffer(data)) { + const err = new ERR_INVALID_ARG_TYPE('data', 'Buffer', data); + return this.emit('error', err); + } + + if (!Buffer.isBuffer(this.handle)) { + return this.once('open', function() { + this._write(data, encoding, cb); + }); + } + + this.sftp.writeData(this.handle, + data, + 0, + data.length, + this.pos, + (er, bytes) => { + if (er) { + if (this.autoClose) + this.destroy(); + return cb(er); + } + this.bytesWritten += bytes; + cb(); + }); + + this.pos += data.length; +}; + +WriteStream.prototype._writev = function(data, cb) { + if (!Buffer.isBuffer(this.handle)) { + return this.once('open', function() { + this._writev(data, cb); + }); + } + + var sftp = this.sftp; + var handle = this.handle; + var writesLeft = data.length; + + var onwrite = (er, bytes) => { + if (er) { + this.destroy(); + return cb(er); + } + this.bytesWritten += bytes; + if (--writesLeft === 0) + cb(); + }; + + // TODO: try to combine chunks to reduce number of requests to the server + for (var i = 0; i < data.length; ++i) { + var chunk = data[i].chunk; + + sftp.writeData(handle, chunk, 0, chunk.length, this.pos, onwrite); + this.pos += chunk.length; + } +}; + +if (typeof WritableStream.prototype.destroy !== 'function') + WriteStream.prototype.destroy = ReadStream.prototype.destroy; + +WriteStream.prototype._destroy = ReadStream.prototype._destroy; +WriteStream.prototype.close = function(cb) { + if (cb) { + if (this.closed) { + process.nextTick(cb); + return; + } else { + this.on('close', cb); + } + } + + // If we are not autoClosing, we should call + // destroy on 'finish'. + if (!this.autoClose) + this.on('finish', this.destroy.bind(this)); + + this.end(); +}; + +// There is no shutdown() for files. +WriteStream.prototype.destroySoon = WriteStream.prototype.end; + +Object.defineProperty(WriteStream.prototype, 'pending', { + get() { return this.handle === null; }, + configurable: true +}); + +module.exports = SFTPStream; + + + +/***/ }), + +/***/ 176: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } + +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +var ERR_INVALID_ARG_TYPE = __webpack_require__(563).codes.ERR_INVALID_ARG_TYPE; + +function from(Readable, iterable, opts) { + var iterator; + + if (iterable && typeof iterable.next === 'function') { + iterator = iterable; + } else if (iterable && iterable[Symbol.asyncIterator]) iterator = iterable[Symbol.asyncIterator]();else if (iterable && iterable[Symbol.iterator]) iterator = iterable[Symbol.iterator]();else throw new ERR_INVALID_ARG_TYPE('iterable', ['Iterable'], iterable); + + var readable = new Readable(_objectSpread({ + objectMode: true + }, opts)); // Reading boolean to protect against _read + // being called before last iteration completion. + + var reading = false; + + readable._read = function () { + if (!reading) { + reading = true; + next(); + } + }; + + function next() { + return _next2.apply(this, arguments); + } + + function _next2() { + _next2 = _asyncToGenerator(function* () { + try { + var _ref = yield iterator.next(), + value = _ref.value, + done = _ref.done; + + if (done) { + readable.push(null); + } else if (readable.push((yield value))) { + next(); + } else { + reading = false; + } + } catch (err) { + readable.destroy(err); + } + }); + return _next2.apply(this, arguments); + } + + return readable; +} + +module.exports = from; + +/***/ }), + +/***/ 180: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents a config + * @param {Object} modem docker-modem + * @param {String} id Config's id + */ +var Config = function(modem, id) { + this.modem = modem; + this.id = id; +}; + +Config.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Inspect + * @param {Function} callback Callback, if specified Docker will be queried. + * @return {Object} Name only if callback isn't specified. + */ +Config.prototype.inspect = function(callback) { + var self = this; + + var optsf = { + path: '/configs/' + this.id, + method: 'GET', + statusCodes: { + 200: true, + 404: 'config not found', + 500: 'server error', + 503: 'node is not part of a swarm' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Update a config. + * + * @param {object} opts + * @param {function} callback + */ +Config.prototype.update = function(opts, callback) { + var self = this; + if (!callback && typeof opts === 'function') { + callback = opts; + } + + var optsf = { + path: '/configs/' + this.id + '/update?', + method: 'POST', + statusCodes: { + 200: true, + 404: 'config not found', + 500: 'server error', + 503: 'node is not part of a swarm' + }, + options: opts + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + + +/** + * Removes the config + * @param {[Object]} opts Remove options (optional) + * @param {Function} callback Callback + */ +Config.prototype.remove = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/configs/' + this.id, + method: 'DELETE', + statusCodes: { + 200: true, + 204: true, + 404: 'config not found', + 500: 'server error', + 503: 'node is not part of a swarm' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + + +module.exports = Config; + + +/***/ }), + +/***/ 191: +/***/ (function(module) { + +module.exports = require("querystring"); + +/***/ }), + +/***/ 196: +/***/ (function(module, __unusedexports, __webpack_require__) { + +(function(nacl) { +'use strict'; + +// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri. +// Public domain. +// +// Implementation derived from TweetNaCl version 20140427. +// See for details: http://tweetnacl.cr.yp.to/ + +var gf = function(init) { + var i, r = new Float64Array(16); + if (init) for (i = 0; i < init.length; i++) r[i] = init[i]; + return r; +}; + +// Pluggable, initialized in high-level API below. +var randombytes = function(/* x, n */) { throw new Error('no PRNG'); }; + +var _0 = new Uint8Array(16); +var _9 = new Uint8Array(32); _9[0] = 9; + +var gf0 = gf(), + gf1 = gf([1]), + _121665 = gf([0xdb41, 1]), + D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]), + D2 = gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]), + X = gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]), + Y = gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]), + I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]); + +function ts64(x, i, h, l) { + x[i] = (h >> 24) & 0xff; + x[i+1] = (h >> 16) & 0xff; + x[i+2] = (h >> 8) & 0xff; + x[i+3] = h & 0xff; + x[i+4] = (l >> 24) & 0xff; + x[i+5] = (l >> 16) & 0xff; + x[i+6] = (l >> 8) & 0xff; + x[i+7] = l & 0xff; +} + +function vn(x, xi, y, yi, n) { + var i,d = 0; + for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i]; + return (1 & ((d - 1) >>> 8)) - 1; +} + +function crypto_verify_16(x, xi, y, yi) { + return vn(x,xi,y,yi,16); +} + +function crypto_verify_32(x, xi, y, yi) { + return vn(x,xi,y,yi,32); +} + +function core_salsa20(o, p, k, c) { + var j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24, + j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24, + j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24, + j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24, + j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24, + j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24, + j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24, + j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24, + j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24, + j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24, + j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24, + j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24, + j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24, + j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24, + j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24, + j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24; + + var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, + x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, + x15 = j15, u; + + for (var i = 0; i < 20; i += 2) { + u = x0 + x12 | 0; + x4 ^= u<<7 | u>>>(32-7); + u = x4 + x0 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x4 | 0; + x12 ^= u<<13 | u>>>(32-13); + u = x12 + x8 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x1 | 0; + x9 ^= u<<7 | u>>>(32-7); + u = x9 + x5 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x9 | 0; + x1 ^= u<<13 | u>>>(32-13); + u = x1 + x13 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x6 | 0; + x14 ^= u<<7 | u>>>(32-7); + u = x14 + x10 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x14 | 0; + x6 ^= u<<13 | u>>>(32-13); + u = x6 + x2 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x11 | 0; + x3 ^= u<<7 | u>>>(32-7); + u = x3 + x15 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x3 | 0; + x11 ^= u<<13 | u>>>(32-13); + u = x11 + x7 | 0; + x15 ^= u<<18 | u>>>(32-18); + + u = x0 + x3 | 0; + x1 ^= u<<7 | u>>>(32-7); + u = x1 + x0 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x1 | 0; + x3 ^= u<<13 | u>>>(32-13); + u = x3 + x2 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x4 | 0; + x6 ^= u<<7 | u>>>(32-7); + u = x6 + x5 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x6 | 0; + x4 ^= u<<13 | u>>>(32-13); + u = x4 + x7 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x9 | 0; + x11 ^= u<<7 | u>>>(32-7); + u = x11 + x10 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x11 | 0; + x9 ^= u<<13 | u>>>(32-13); + u = x9 + x8 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x14 | 0; + x12 ^= u<<7 | u>>>(32-7); + u = x12 + x15 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x12 | 0; + x14 ^= u<<13 | u>>>(32-13); + u = x14 + x13 | 0; + x15 ^= u<<18 | u>>>(32-18); + } + x0 = x0 + j0 | 0; + x1 = x1 + j1 | 0; + x2 = x2 + j2 | 0; + x3 = x3 + j3 | 0; + x4 = x4 + j4 | 0; + x5 = x5 + j5 | 0; + x6 = x6 + j6 | 0; + x7 = x7 + j7 | 0; + x8 = x8 + j8 | 0; + x9 = x9 + j9 | 0; + x10 = x10 + j10 | 0; + x11 = x11 + j11 | 0; + x12 = x12 + j12 | 0; + x13 = x13 + j13 | 0; + x14 = x14 + j14 | 0; + x15 = x15 + j15 | 0; + + o[ 0] = x0 >>> 0 & 0xff; + o[ 1] = x0 >>> 8 & 0xff; + o[ 2] = x0 >>> 16 & 0xff; + o[ 3] = x0 >>> 24 & 0xff; + + o[ 4] = x1 >>> 0 & 0xff; + o[ 5] = x1 >>> 8 & 0xff; + o[ 6] = x1 >>> 16 & 0xff; + o[ 7] = x1 >>> 24 & 0xff; + + o[ 8] = x2 >>> 0 & 0xff; + o[ 9] = x2 >>> 8 & 0xff; + o[10] = x2 >>> 16 & 0xff; + o[11] = x2 >>> 24 & 0xff; + + o[12] = x3 >>> 0 & 0xff; + o[13] = x3 >>> 8 & 0xff; + o[14] = x3 >>> 16 & 0xff; + o[15] = x3 >>> 24 & 0xff; + + o[16] = x4 >>> 0 & 0xff; + o[17] = x4 >>> 8 & 0xff; + o[18] = x4 >>> 16 & 0xff; + o[19] = x4 >>> 24 & 0xff; + + o[20] = x5 >>> 0 & 0xff; + o[21] = x5 >>> 8 & 0xff; + o[22] = x5 >>> 16 & 0xff; + o[23] = x5 >>> 24 & 0xff; + + o[24] = x6 >>> 0 & 0xff; + o[25] = x6 >>> 8 & 0xff; + o[26] = x6 >>> 16 & 0xff; + o[27] = x6 >>> 24 & 0xff; + + o[28] = x7 >>> 0 & 0xff; + o[29] = x7 >>> 8 & 0xff; + o[30] = x7 >>> 16 & 0xff; + o[31] = x7 >>> 24 & 0xff; + + o[32] = x8 >>> 0 & 0xff; + o[33] = x8 >>> 8 & 0xff; + o[34] = x8 >>> 16 & 0xff; + o[35] = x8 >>> 24 & 0xff; + + o[36] = x9 >>> 0 & 0xff; + o[37] = x9 >>> 8 & 0xff; + o[38] = x9 >>> 16 & 0xff; + o[39] = x9 >>> 24 & 0xff; + + o[40] = x10 >>> 0 & 0xff; + o[41] = x10 >>> 8 & 0xff; + o[42] = x10 >>> 16 & 0xff; + o[43] = x10 >>> 24 & 0xff; + + o[44] = x11 >>> 0 & 0xff; + o[45] = x11 >>> 8 & 0xff; + o[46] = x11 >>> 16 & 0xff; + o[47] = x11 >>> 24 & 0xff; + + o[48] = x12 >>> 0 & 0xff; + o[49] = x12 >>> 8 & 0xff; + o[50] = x12 >>> 16 & 0xff; + o[51] = x12 >>> 24 & 0xff; + + o[52] = x13 >>> 0 & 0xff; + o[53] = x13 >>> 8 & 0xff; + o[54] = x13 >>> 16 & 0xff; + o[55] = x13 >>> 24 & 0xff; + + o[56] = x14 >>> 0 & 0xff; + o[57] = x14 >>> 8 & 0xff; + o[58] = x14 >>> 16 & 0xff; + o[59] = x14 >>> 24 & 0xff; + + o[60] = x15 >>> 0 & 0xff; + o[61] = x15 >>> 8 & 0xff; + o[62] = x15 >>> 16 & 0xff; + o[63] = x15 >>> 24 & 0xff; +} + +function core_hsalsa20(o,p,k,c) { + var j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24, + j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24, + j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24, + j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24, + j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24, + j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24, + j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24, + j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24, + j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24, + j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24, + j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24, + j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24, + j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24, + j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24, + j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24, + j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24; + + var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, + x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, + x15 = j15, u; + + for (var i = 0; i < 20; i += 2) { + u = x0 + x12 | 0; + x4 ^= u<<7 | u>>>(32-7); + u = x4 + x0 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x4 | 0; + x12 ^= u<<13 | u>>>(32-13); + u = x12 + x8 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x1 | 0; + x9 ^= u<<7 | u>>>(32-7); + u = x9 + x5 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x9 | 0; + x1 ^= u<<13 | u>>>(32-13); + u = x1 + x13 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x6 | 0; + x14 ^= u<<7 | u>>>(32-7); + u = x14 + x10 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x14 | 0; + x6 ^= u<<13 | u>>>(32-13); + u = x6 + x2 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x11 | 0; + x3 ^= u<<7 | u>>>(32-7); + u = x3 + x15 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x3 | 0; + x11 ^= u<<13 | u>>>(32-13); + u = x11 + x7 | 0; + x15 ^= u<<18 | u>>>(32-18); + + u = x0 + x3 | 0; + x1 ^= u<<7 | u>>>(32-7); + u = x1 + x0 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x1 | 0; + x3 ^= u<<13 | u>>>(32-13); + u = x3 + x2 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x4 | 0; + x6 ^= u<<7 | u>>>(32-7); + u = x6 + x5 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x6 | 0; + x4 ^= u<<13 | u>>>(32-13); + u = x4 + x7 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x9 | 0; + x11 ^= u<<7 | u>>>(32-7); + u = x11 + x10 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x11 | 0; + x9 ^= u<<13 | u>>>(32-13); + u = x9 + x8 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x14 | 0; + x12 ^= u<<7 | u>>>(32-7); + u = x12 + x15 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x12 | 0; + x14 ^= u<<13 | u>>>(32-13); + u = x14 + x13 | 0; + x15 ^= u<<18 | u>>>(32-18); + } + + o[ 0] = x0 >>> 0 & 0xff; + o[ 1] = x0 >>> 8 & 0xff; + o[ 2] = x0 >>> 16 & 0xff; + o[ 3] = x0 >>> 24 & 0xff; + + o[ 4] = x5 >>> 0 & 0xff; + o[ 5] = x5 >>> 8 & 0xff; + o[ 6] = x5 >>> 16 & 0xff; + o[ 7] = x5 >>> 24 & 0xff; + + o[ 8] = x10 >>> 0 & 0xff; + o[ 9] = x10 >>> 8 & 0xff; + o[10] = x10 >>> 16 & 0xff; + o[11] = x10 >>> 24 & 0xff; + + o[12] = x15 >>> 0 & 0xff; + o[13] = x15 >>> 8 & 0xff; + o[14] = x15 >>> 16 & 0xff; + o[15] = x15 >>> 24 & 0xff; + + o[16] = x6 >>> 0 & 0xff; + o[17] = x6 >>> 8 & 0xff; + o[18] = x6 >>> 16 & 0xff; + o[19] = x6 >>> 24 & 0xff; + + o[20] = x7 >>> 0 & 0xff; + o[21] = x7 >>> 8 & 0xff; + o[22] = x7 >>> 16 & 0xff; + o[23] = x7 >>> 24 & 0xff; + + o[24] = x8 >>> 0 & 0xff; + o[25] = x8 >>> 8 & 0xff; + o[26] = x8 >>> 16 & 0xff; + o[27] = x8 >>> 24 & 0xff; + + o[28] = x9 >>> 0 & 0xff; + o[29] = x9 >>> 8 & 0xff; + o[30] = x9 >>> 16 & 0xff; + o[31] = x9 >>> 24 & 0xff; +} + +function crypto_core_salsa20(out,inp,k,c) { + core_salsa20(out,inp,k,c); +} + +function crypto_core_hsalsa20(out,inp,k,c) { + core_hsalsa20(out,inp,k,c); +} + +var sigma = new Uint8Array([101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107]); + // "expand 32-byte k" + +function crypto_stream_salsa20_xor(c,cpos,m,mpos,b,n,k) { + var z = new Uint8Array(16), x = new Uint8Array(64); + var u, i; + for (i = 0; i < 16; i++) z[i] = 0; + for (i = 0; i < 8; i++) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < 64; i++) c[cpos+i] = m[mpos+i] ^ x[i]; + u = 1; + for (i = 8; i < 16; i++) { + u = u + (z[i] & 0xff) | 0; + z[i] = u & 0xff; + u >>>= 8; + } + b -= 64; + cpos += 64; + mpos += 64; + } + if (b > 0) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < b; i++) c[cpos+i] = m[mpos+i] ^ x[i]; + } + return 0; +} + +function crypto_stream_salsa20(c,cpos,b,n,k) { + var z = new Uint8Array(16), x = new Uint8Array(64); + var u, i; + for (i = 0; i < 16; i++) z[i] = 0; + for (i = 0; i < 8; i++) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < 64; i++) c[cpos+i] = x[i]; + u = 1; + for (i = 8; i < 16; i++) { + u = u + (z[i] & 0xff) | 0; + z[i] = u & 0xff; + u >>>= 8; + } + b -= 64; + cpos += 64; + } + if (b > 0) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < b; i++) c[cpos+i] = x[i]; + } + return 0; +} + +function crypto_stream(c,cpos,d,n,k) { + var s = new Uint8Array(32); + crypto_core_hsalsa20(s,n,k,sigma); + var sn = new Uint8Array(8); + for (var i = 0; i < 8; i++) sn[i] = n[i+16]; + return crypto_stream_salsa20(c,cpos,d,sn,s); +} + +function crypto_stream_xor(c,cpos,m,mpos,d,n,k) { + var s = new Uint8Array(32); + crypto_core_hsalsa20(s,n,k,sigma); + var sn = new Uint8Array(8); + for (var i = 0; i < 8; i++) sn[i] = n[i+16]; + return crypto_stream_salsa20_xor(c,cpos,m,mpos,d,sn,s); +} + +/* +* Port of Andrew Moon's Poly1305-donna-16. Public domain. +* https://github.com/floodyberry/poly1305-donna +*/ + +var poly1305 = function(key) { + this.buffer = new Uint8Array(16); + this.r = new Uint16Array(10); + this.h = new Uint16Array(10); + this.pad = new Uint16Array(8); + this.leftover = 0; + this.fin = 0; + + var t0, t1, t2, t3, t4, t5, t6, t7; + + t0 = key[ 0] & 0xff | (key[ 1] & 0xff) << 8; this.r[0] = ( t0 ) & 0x1fff; + t1 = key[ 2] & 0xff | (key[ 3] & 0xff) << 8; this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff; + t2 = key[ 4] & 0xff | (key[ 5] & 0xff) << 8; this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03; + t3 = key[ 6] & 0xff | (key[ 7] & 0xff) << 8; this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff; + t4 = key[ 8] & 0xff | (key[ 9] & 0xff) << 8; this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff; + this.r[5] = ((t4 >>> 1)) & 0x1ffe; + t5 = key[10] & 0xff | (key[11] & 0xff) << 8; this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff; + t6 = key[12] & 0xff | (key[13] & 0xff) << 8; this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81; + t7 = key[14] & 0xff | (key[15] & 0xff) << 8; this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff; + this.r[9] = ((t7 >>> 5)) & 0x007f; + + this.pad[0] = key[16] & 0xff | (key[17] & 0xff) << 8; + this.pad[1] = key[18] & 0xff | (key[19] & 0xff) << 8; + this.pad[2] = key[20] & 0xff | (key[21] & 0xff) << 8; + this.pad[3] = key[22] & 0xff | (key[23] & 0xff) << 8; + this.pad[4] = key[24] & 0xff | (key[25] & 0xff) << 8; + this.pad[5] = key[26] & 0xff | (key[27] & 0xff) << 8; + this.pad[6] = key[28] & 0xff | (key[29] & 0xff) << 8; + this.pad[7] = key[30] & 0xff | (key[31] & 0xff) << 8; +}; + +poly1305.prototype.blocks = function(m, mpos, bytes) { + var hibit = this.fin ? 0 : (1 << 11); + var t0, t1, t2, t3, t4, t5, t6, t7, c; + var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9; + + var h0 = this.h[0], + h1 = this.h[1], + h2 = this.h[2], + h3 = this.h[3], + h4 = this.h[4], + h5 = this.h[5], + h6 = this.h[6], + h7 = this.h[7], + h8 = this.h[8], + h9 = this.h[9]; + + var r0 = this.r[0], + r1 = this.r[1], + r2 = this.r[2], + r3 = this.r[3], + r4 = this.r[4], + r5 = this.r[5], + r6 = this.r[6], + r7 = this.r[7], + r8 = this.r[8], + r9 = this.r[9]; + + while (bytes >= 16) { + t0 = m[mpos+ 0] & 0xff | (m[mpos+ 1] & 0xff) << 8; h0 += ( t0 ) & 0x1fff; + t1 = m[mpos+ 2] & 0xff | (m[mpos+ 3] & 0xff) << 8; h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff; + t2 = m[mpos+ 4] & 0xff | (m[mpos+ 5] & 0xff) << 8; h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff; + t3 = m[mpos+ 6] & 0xff | (m[mpos+ 7] & 0xff) << 8; h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff; + t4 = m[mpos+ 8] & 0xff | (m[mpos+ 9] & 0xff) << 8; h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff; + h5 += ((t4 >>> 1)) & 0x1fff; + t5 = m[mpos+10] & 0xff | (m[mpos+11] & 0xff) << 8; h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff; + t6 = m[mpos+12] & 0xff | (m[mpos+13] & 0xff) << 8; h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff; + t7 = m[mpos+14] & 0xff | (m[mpos+15] & 0xff) << 8; h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff; + h9 += ((t7 >>> 5)) | hibit; + + c = 0; + + d0 = c; + d0 += h0 * r0; + d0 += h1 * (5 * r9); + d0 += h2 * (5 * r8); + d0 += h3 * (5 * r7); + d0 += h4 * (5 * r6); + c = (d0 >>> 13); d0 &= 0x1fff; + d0 += h5 * (5 * r5); + d0 += h6 * (5 * r4); + d0 += h7 * (5 * r3); + d0 += h8 * (5 * r2); + d0 += h9 * (5 * r1); + c += (d0 >>> 13); d0 &= 0x1fff; + + d1 = c; + d1 += h0 * r1; + d1 += h1 * r0; + d1 += h2 * (5 * r9); + d1 += h3 * (5 * r8); + d1 += h4 * (5 * r7); + c = (d1 >>> 13); d1 &= 0x1fff; + d1 += h5 * (5 * r6); + d1 += h6 * (5 * r5); + d1 += h7 * (5 * r4); + d1 += h8 * (5 * r3); + d1 += h9 * (5 * r2); + c += (d1 >>> 13); d1 &= 0x1fff; + + d2 = c; + d2 += h0 * r2; + d2 += h1 * r1; + d2 += h2 * r0; + d2 += h3 * (5 * r9); + d2 += h4 * (5 * r8); + c = (d2 >>> 13); d2 &= 0x1fff; + d2 += h5 * (5 * r7); + d2 += h6 * (5 * r6); + d2 += h7 * (5 * r5); + d2 += h8 * (5 * r4); + d2 += h9 * (5 * r3); + c += (d2 >>> 13); d2 &= 0x1fff; + + d3 = c; + d3 += h0 * r3; + d3 += h1 * r2; + d3 += h2 * r1; + d3 += h3 * r0; + d3 += h4 * (5 * r9); + c = (d3 >>> 13); d3 &= 0x1fff; + d3 += h5 * (5 * r8); + d3 += h6 * (5 * r7); + d3 += h7 * (5 * r6); + d3 += h8 * (5 * r5); + d3 += h9 * (5 * r4); + c += (d3 >>> 13); d3 &= 0x1fff; + + d4 = c; + d4 += h0 * r4; + d4 += h1 * r3; + d4 += h2 * r2; + d4 += h3 * r1; + d4 += h4 * r0; + c = (d4 >>> 13); d4 &= 0x1fff; + d4 += h5 * (5 * r9); + d4 += h6 * (5 * r8); + d4 += h7 * (5 * r7); + d4 += h8 * (5 * r6); + d4 += h9 * (5 * r5); + c += (d4 >>> 13); d4 &= 0x1fff; + + d5 = c; + d5 += h0 * r5; + d5 += h1 * r4; + d5 += h2 * r3; + d5 += h3 * r2; + d5 += h4 * r1; + c = (d5 >>> 13); d5 &= 0x1fff; + d5 += h5 * r0; + d5 += h6 * (5 * r9); + d5 += h7 * (5 * r8); + d5 += h8 * (5 * r7); + d5 += h9 * (5 * r6); + c += (d5 >>> 13); d5 &= 0x1fff; + + d6 = c; + d6 += h0 * r6; + d6 += h1 * r5; + d6 += h2 * r4; + d6 += h3 * r3; + d6 += h4 * r2; + c = (d6 >>> 13); d6 &= 0x1fff; + d6 += h5 * r1; + d6 += h6 * r0; + d6 += h7 * (5 * r9); + d6 += h8 * (5 * r8); + d6 += h9 * (5 * r7); + c += (d6 >>> 13); d6 &= 0x1fff; + + d7 = c; + d7 += h0 * r7; + d7 += h1 * r6; + d7 += h2 * r5; + d7 += h3 * r4; + d7 += h4 * r3; + c = (d7 >>> 13); d7 &= 0x1fff; + d7 += h5 * r2; + d7 += h6 * r1; + d7 += h7 * r0; + d7 += h8 * (5 * r9); + d7 += h9 * (5 * r8); + c += (d7 >>> 13); d7 &= 0x1fff; + + d8 = c; + d8 += h0 * r8; + d8 += h1 * r7; + d8 += h2 * r6; + d8 += h3 * r5; + d8 += h4 * r4; + c = (d8 >>> 13); d8 &= 0x1fff; + d8 += h5 * r3; + d8 += h6 * r2; + d8 += h7 * r1; + d8 += h8 * r0; + d8 += h9 * (5 * r9); + c += (d8 >>> 13); d8 &= 0x1fff; + + d9 = c; + d9 += h0 * r9; + d9 += h1 * r8; + d9 += h2 * r7; + d9 += h3 * r6; + d9 += h4 * r5; + c = (d9 >>> 13); d9 &= 0x1fff; + d9 += h5 * r4; + d9 += h6 * r3; + d9 += h7 * r2; + d9 += h8 * r1; + d9 += h9 * r0; + c += (d9 >>> 13); d9 &= 0x1fff; + + c = (((c << 2) + c)) | 0; + c = (c + d0) | 0; + d0 = c & 0x1fff; + c = (c >>> 13); + d1 += c; + + h0 = d0; + h1 = d1; + h2 = d2; + h3 = d3; + h4 = d4; + h5 = d5; + h6 = d6; + h7 = d7; + h8 = d8; + h9 = d9; + + mpos += 16; + bytes -= 16; + } + this.h[0] = h0; + this.h[1] = h1; + this.h[2] = h2; + this.h[3] = h3; + this.h[4] = h4; + this.h[5] = h5; + this.h[6] = h6; + this.h[7] = h7; + this.h[8] = h8; + this.h[9] = h9; +}; + +poly1305.prototype.finish = function(mac, macpos) { + var g = new Uint16Array(10); + var c, mask, f, i; + + if (this.leftover) { + i = this.leftover; + this.buffer[i++] = 1; + for (; i < 16; i++) this.buffer[i] = 0; + this.fin = 1; + this.blocks(this.buffer, 0, 16); + } + + c = this.h[1] >>> 13; + this.h[1] &= 0x1fff; + for (i = 2; i < 10; i++) { + this.h[i] += c; + c = this.h[i] >>> 13; + this.h[i] &= 0x1fff; + } + this.h[0] += (c * 5); + c = this.h[0] >>> 13; + this.h[0] &= 0x1fff; + this.h[1] += c; + c = this.h[1] >>> 13; + this.h[1] &= 0x1fff; + this.h[2] += c; + + g[0] = this.h[0] + 5; + c = g[0] >>> 13; + g[0] &= 0x1fff; + for (i = 1; i < 10; i++) { + g[i] = this.h[i] + c; + c = g[i] >>> 13; + g[i] &= 0x1fff; + } + g[9] -= (1 << 13); + + mask = (c ^ 1) - 1; + for (i = 0; i < 10; i++) g[i] &= mask; + mask = ~mask; + for (i = 0; i < 10; i++) this.h[i] = (this.h[i] & mask) | g[i]; + + this.h[0] = ((this.h[0] ) | (this.h[1] << 13) ) & 0xffff; + this.h[1] = ((this.h[1] >>> 3) | (this.h[2] << 10) ) & 0xffff; + this.h[2] = ((this.h[2] >>> 6) | (this.h[3] << 7) ) & 0xffff; + this.h[3] = ((this.h[3] >>> 9) | (this.h[4] << 4) ) & 0xffff; + this.h[4] = ((this.h[4] >>> 12) | (this.h[5] << 1) | (this.h[6] << 14)) & 0xffff; + this.h[5] = ((this.h[6] >>> 2) | (this.h[7] << 11) ) & 0xffff; + this.h[6] = ((this.h[7] >>> 5) | (this.h[8] << 8) ) & 0xffff; + this.h[7] = ((this.h[8] >>> 8) | (this.h[9] << 5) ) & 0xffff; + + f = this.h[0] + this.pad[0]; + this.h[0] = f & 0xffff; + for (i = 1; i < 8; i++) { + f = (((this.h[i] + this.pad[i]) | 0) + (f >>> 16)) | 0; + this.h[i] = f & 0xffff; + } + + mac[macpos+ 0] = (this.h[0] >>> 0) & 0xff; + mac[macpos+ 1] = (this.h[0] >>> 8) & 0xff; + mac[macpos+ 2] = (this.h[1] >>> 0) & 0xff; + mac[macpos+ 3] = (this.h[1] >>> 8) & 0xff; + mac[macpos+ 4] = (this.h[2] >>> 0) & 0xff; + mac[macpos+ 5] = (this.h[2] >>> 8) & 0xff; + mac[macpos+ 6] = (this.h[3] >>> 0) & 0xff; + mac[macpos+ 7] = (this.h[3] >>> 8) & 0xff; + mac[macpos+ 8] = (this.h[4] >>> 0) & 0xff; + mac[macpos+ 9] = (this.h[4] >>> 8) & 0xff; + mac[macpos+10] = (this.h[5] >>> 0) & 0xff; + mac[macpos+11] = (this.h[5] >>> 8) & 0xff; + mac[macpos+12] = (this.h[6] >>> 0) & 0xff; + mac[macpos+13] = (this.h[6] >>> 8) & 0xff; + mac[macpos+14] = (this.h[7] >>> 0) & 0xff; + mac[macpos+15] = (this.h[7] >>> 8) & 0xff; +}; + +poly1305.prototype.update = function(m, mpos, bytes) { + var i, want; + + if (this.leftover) { + want = (16 - this.leftover); + if (want > bytes) + want = bytes; + for (i = 0; i < want; i++) + this.buffer[this.leftover + i] = m[mpos+i]; + bytes -= want; + mpos += want; + this.leftover += want; + if (this.leftover < 16) + return; + this.blocks(this.buffer, 0, 16); + this.leftover = 0; + } + + if (bytes >= 16) { + want = bytes - (bytes % 16); + this.blocks(m, mpos, want); + mpos += want; + bytes -= want; + } + + if (bytes) { + for (i = 0; i < bytes; i++) + this.buffer[this.leftover + i] = m[mpos+i]; + this.leftover += bytes; + } +}; + +function crypto_onetimeauth(out, outpos, m, mpos, n, k) { + var s = new poly1305(k); + s.update(m, mpos, n); + s.finish(out, outpos); + return 0; +} + +function crypto_onetimeauth_verify(h, hpos, m, mpos, n, k) { + var x = new Uint8Array(16); + crypto_onetimeauth(x,0,m,mpos,n,k); + return crypto_verify_16(h,hpos,x,0); +} + +function crypto_secretbox(c,m,d,n,k) { + var i; + if (d < 32) return -1; + crypto_stream_xor(c,0,m,0,d,n,k); + crypto_onetimeauth(c, 16, c, 32, d - 32, c); + for (i = 0; i < 16; i++) c[i] = 0; + return 0; +} + +function crypto_secretbox_open(m,c,d,n,k) { + var i; + var x = new Uint8Array(32); + if (d < 32) return -1; + crypto_stream(x,0,32,n,k); + if (crypto_onetimeauth_verify(c, 16,c, 32,d - 32,x) !== 0) return -1; + crypto_stream_xor(m,0,c,0,d,n,k); + for (i = 0; i < 32; i++) m[i] = 0; + return 0; +} + +function set25519(r, a) { + var i; + for (i = 0; i < 16; i++) r[i] = a[i]|0; +} + +function car25519(o) { + var i, v, c = 1; + for (i = 0; i < 16; i++) { + v = o[i] + c + 65535; + c = Math.floor(v / 65536); + o[i] = v - c * 65536; + } + o[0] += c-1 + 37 * (c-1); +} + +function sel25519(p, q, b) { + var t, c = ~(b-1); + for (var i = 0; i < 16; i++) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } +} + +function pack25519(o, n) { + var i, j, b; + var m = gf(), t = gf(); + for (i = 0; i < 16; i++) t[i] = n[i]; + car25519(t); + car25519(t); + car25519(t); + for (j = 0; j < 2; j++) { + m[0] = t[0] - 0xffed; + for (i = 1; i < 15; i++) { + m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1); + m[i-1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1); + b = (m[15]>>16) & 1; + m[14] &= 0xffff; + sel25519(t, m, 1-b); + } + for (i = 0; i < 16; i++) { + o[2*i] = t[i] & 0xff; + o[2*i+1] = t[i]>>8; + } +} + +function neq25519(a, b) { + var c = new Uint8Array(32), d = new Uint8Array(32); + pack25519(c, a); + pack25519(d, b); + return crypto_verify_32(c, 0, d, 0); +} + +function par25519(a) { + var d = new Uint8Array(32); + pack25519(d, a); + return d[0] & 1; +} + +function unpack25519(o, n) { + var i; + for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8); + o[15] &= 0x7fff; +} + +function A(o, a, b) { + for (var i = 0; i < 16; i++) o[i] = a[i] + b[i]; +} + +function Z(o, a, b) { + for (var i = 0; i < 16; i++) o[i] = a[i] - b[i]; +} + +function M(o, a, b) { + var v, c, + t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, + t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, + t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, + t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, + b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3], + b4 = b[4], + b5 = b[5], + b6 = b[6], + b7 = b[7], + b8 = b[8], + b9 = b[9], + b10 = b[10], + b11 = b[11], + b12 = b[12], + b13 = b[13], + b14 = b[14], + b15 = b[15]; + + v = a[0]; + t0 += v * b0; + t1 += v * b1; + t2 += v * b2; + t3 += v * b3; + t4 += v * b4; + t5 += v * b5; + t6 += v * b6; + t7 += v * b7; + t8 += v * b8; + t9 += v * b9; + t10 += v * b10; + t11 += v * b11; + t12 += v * b12; + t13 += v * b13; + t14 += v * b14; + t15 += v * b15; + v = a[1]; + t1 += v * b0; + t2 += v * b1; + t3 += v * b2; + t4 += v * b3; + t5 += v * b4; + t6 += v * b5; + t7 += v * b6; + t8 += v * b7; + t9 += v * b8; + t10 += v * b9; + t11 += v * b10; + t12 += v * b11; + t13 += v * b12; + t14 += v * b13; + t15 += v * b14; + t16 += v * b15; + v = a[2]; + t2 += v * b0; + t3 += v * b1; + t4 += v * b2; + t5 += v * b3; + t6 += v * b4; + t7 += v * b5; + t8 += v * b6; + t9 += v * b7; + t10 += v * b8; + t11 += v * b9; + t12 += v * b10; + t13 += v * b11; + t14 += v * b12; + t15 += v * b13; + t16 += v * b14; + t17 += v * b15; + v = a[3]; + t3 += v * b0; + t4 += v * b1; + t5 += v * b2; + t6 += v * b3; + t7 += v * b4; + t8 += v * b5; + t9 += v * b6; + t10 += v * b7; + t11 += v * b8; + t12 += v * b9; + t13 += v * b10; + t14 += v * b11; + t15 += v * b12; + t16 += v * b13; + t17 += v * b14; + t18 += v * b15; + v = a[4]; + t4 += v * b0; + t5 += v * b1; + t6 += v * b2; + t7 += v * b3; + t8 += v * b4; + t9 += v * b5; + t10 += v * b6; + t11 += v * b7; + t12 += v * b8; + t13 += v * b9; + t14 += v * b10; + t15 += v * b11; + t16 += v * b12; + t17 += v * b13; + t18 += v * b14; + t19 += v * b15; + v = a[5]; + t5 += v * b0; + t6 += v * b1; + t7 += v * b2; + t8 += v * b3; + t9 += v * b4; + t10 += v * b5; + t11 += v * b6; + t12 += v * b7; + t13 += v * b8; + t14 += v * b9; + t15 += v * b10; + t16 += v * b11; + t17 += v * b12; + t18 += v * b13; + t19 += v * b14; + t20 += v * b15; + v = a[6]; + t6 += v * b0; + t7 += v * b1; + t8 += v * b2; + t9 += v * b3; + t10 += v * b4; + t11 += v * b5; + t12 += v * b6; + t13 += v * b7; + t14 += v * b8; + t15 += v * b9; + t16 += v * b10; + t17 += v * b11; + t18 += v * b12; + t19 += v * b13; + t20 += v * b14; + t21 += v * b15; + v = a[7]; + t7 += v * b0; + t8 += v * b1; + t9 += v * b2; + t10 += v * b3; + t11 += v * b4; + t12 += v * b5; + t13 += v * b6; + t14 += v * b7; + t15 += v * b8; + t16 += v * b9; + t17 += v * b10; + t18 += v * b11; + t19 += v * b12; + t20 += v * b13; + t21 += v * b14; + t22 += v * b15; + v = a[8]; + t8 += v * b0; + t9 += v * b1; + t10 += v * b2; + t11 += v * b3; + t12 += v * b4; + t13 += v * b5; + t14 += v * b6; + t15 += v * b7; + t16 += v * b8; + t17 += v * b9; + t18 += v * b10; + t19 += v * b11; + t20 += v * b12; + t21 += v * b13; + t22 += v * b14; + t23 += v * b15; + v = a[9]; + t9 += v * b0; + t10 += v * b1; + t11 += v * b2; + t12 += v * b3; + t13 += v * b4; + t14 += v * b5; + t15 += v * b6; + t16 += v * b7; + t17 += v * b8; + t18 += v * b9; + t19 += v * b10; + t20 += v * b11; + t21 += v * b12; + t22 += v * b13; + t23 += v * b14; + t24 += v * b15; + v = a[10]; + t10 += v * b0; + t11 += v * b1; + t12 += v * b2; + t13 += v * b3; + t14 += v * b4; + t15 += v * b5; + t16 += v * b6; + t17 += v * b7; + t18 += v * b8; + t19 += v * b9; + t20 += v * b10; + t21 += v * b11; + t22 += v * b12; + t23 += v * b13; + t24 += v * b14; + t25 += v * b15; + v = a[11]; + t11 += v * b0; + t12 += v * b1; + t13 += v * b2; + t14 += v * b3; + t15 += v * b4; + t16 += v * b5; + t17 += v * b6; + t18 += v * b7; + t19 += v * b8; + t20 += v * b9; + t21 += v * b10; + t22 += v * b11; + t23 += v * b12; + t24 += v * b13; + t25 += v * b14; + t26 += v * b15; + v = a[12]; + t12 += v * b0; + t13 += v * b1; + t14 += v * b2; + t15 += v * b3; + t16 += v * b4; + t17 += v * b5; + t18 += v * b6; + t19 += v * b7; + t20 += v * b8; + t21 += v * b9; + t22 += v * b10; + t23 += v * b11; + t24 += v * b12; + t25 += v * b13; + t26 += v * b14; + t27 += v * b15; + v = a[13]; + t13 += v * b0; + t14 += v * b1; + t15 += v * b2; + t16 += v * b3; + t17 += v * b4; + t18 += v * b5; + t19 += v * b6; + t20 += v * b7; + t21 += v * b8; + t22 += v * b9; + t23 += v * b10; + t24 += v * b11; + t25 += v * b12; + t26 += v * b13; + t27 += v * b14; + t28 += v * b15; + v = a[14]; + t14 += v * b0; + t15 += v * b1; + t16 += v * b2; + t17 += v * b3; + t18 += v * b4; + t19 += v * b5; + t20 += v * b6; + t21 += v * b7; + t22 += v * b8; + t23 += v * b9; + t24 += v * b10; + t25 += v * b11; + t26 += v * b12; + t27 += v * b13; + t28 += v * b14; + t29 += v * b15; + v = a[15]; + t15 += v * b0; + t16 += v * b1; + t17 += v * b2; + t18 += v * b3; + t19 += v * b4; + t20 += v * b5; + t21 += v * b6; + t22 += v * b7; + t23 += v * b8; + t24 += v * b9; + t25 += v * b10; + t26 += v * b11; + t27 += v * b12; + t28 += v * b13; + t29 += v * b14; + t30 += v * b15; + + t0 += 38 * t16; + t1 += 38 * t17; + t2 += 38 * t18; + t3 += 38 * t19; + t4 += 38 * t20; + t5 += 38 * t21; + t6 += 38 * t22; + t7 += 38 * t23; + t8 += 38 * t24; + t9 += 38 * t25; + t10 += 38 * t26; + t11 += 38 * t27; + t12 += 38 * t28; + t13 += 38 * t29; + t14 += 38 * t30; + // t15 left as is + + // first car + c = 1; + v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536; + v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536; + v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536; + v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536; + v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536; + v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536; + v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536; + v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536; + v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536; + v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536; + v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536; + v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536; + v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536; + v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536; + v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536; + v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536; + t0 += c-1 + 37 * (c-1); + + // second car + c = 1; + v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536; + v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536; + v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536; + v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536; + v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536; + v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536; + v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536; + v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536; + v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536; + v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536; + v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536; + v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536; + v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536; + v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536; + v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536; + v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536; + t0 += c-1 + 37 * (c-1); + + o[ 0] = t0; + o[ 1] = t1; + o[ 2] = t2; + o[ 3] = t3; + o[ 4] = t4; + o[ 5] = t5; + o[ 6] = t6; + o[ 7] = t7; + o[ 8] = t8; + o[ 9] = t9; + o[10] = t10; + o[11] = t11; + o[12] = t12; + o[13] = t13; + o[14] = t14; + o[15] = t15; +} + +function S(o, a) { + M(o, a, a); +} + +function inv25519(o, i) { + var c = gf(); + var a; + for (a = 0; a < 16; a++) c[a] = i[a]; + for (a = 253; a >= 0; a--) { + S(c, c); + if(a !== 2 && a !== 4) M(c, c, i); + } + for (a = 0; a < 16; a++) o[a] = c[a]; +} + +function pow2523(o, i) { + var c = gf(); + var a; + for (a = 0; a < 16; a++) c[a] = i[a]; + for (a = 250; a >= 0; a--) { + S(c, c); + if(a !== 1) M(c, c, i); + } + for (a = 0; a < 16; a++) o[a] = c[a]; +} + +function crypto_scalarmult(q, n, p) { + var z = new Uint8Array(32); + var x = new Float64Array(80), r, i; + var a = gf(), b = gf(), c = gf(), + d = gf(), e = gf(), f = gf(); + for (i = 0; i < 31; i++) z[i] = n[i]; + z[31]=(n[31]&127)|64; + z[0]&=248; + unpack25519(x,p); + for (i = 0; i < 16; i++) { + b[i]=x[i]; + d[i]=a[i]=c[i]=0; + } + a[0]=d[0]=1; + for (i=254; i>=0; --i) { + r=(z[i>>>3]>>>(i&7))&1; + sel25519(a,b,r); + sel25519(c,d,r); + A(e,a,c); + Z(a,a,c); + A(c,b,d); + Z(b,b,d); + S(d,e); + S(f,a); + M(a,c,a); + M(c,b,e); + A(e,a,c); + Z(a,a,c); + S(b,a); + Z(c,d,f); + M(a,c,_121665); + A(a,a,d); + M(c,c,a); + M(a,d,f); + M(d,b,x); + S(b,e); + sel25519(a,b,r); + sel25519(c,d,r); + } + for (i = 0; i < 16; i++) { + x[i+16]=a[i]; + x[i+32]=c[i]; + x[i+48]=b[i]; + x[i+64]=d[i]; + } + var x32 = x.subarray(32); + var x16 = x.subarray(16); + inv25519(x32,x32); + M(x16,x16,x32); + pack25519(q,x16); + return 0; +} + +function crypto_scalarmult_base(q, n) { + return crypto_scalarmult(q, n, _9); +} + +function crypto_box_keypair(y, x) { + randombytes(x, 32); + return crypto_scalarmult_base(y, x); +} + +function crypto_box_beforenm(k, y, x) { + var s = new Uint8Array(32); + crypto_scalarmult(s, x, y); + return crypto_core_hsalsa20(k, _0, s, sigma); +} + +var crypto_box_afternm = crypto_secretbox; +var crypto_box_open_afternm = crypto_secretbox_open; + +function crypto_box(c, m, d, n, y, x) { + var k = new Uint8Array(32); + crypto_box_beforenm(k, y, x); + return crypto_box_afternm(c, m, d, n, k); +} + +function crypto_box_open(m, c, d, n, y, x) { + var k = new Uint8Array(32); + crypto_box_beforenm(k, y, x); + return crypto_box_open_afternm(m, c, d, n, k); +} + +var K = [ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, + 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, + 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, + 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, + 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, + 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, + 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, + 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, + 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, + 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, + 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, + 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, + 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, + 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, + 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, + 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, + 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, + 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, + 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, + 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, + 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, + 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, + 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, + 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, + 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, + 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, + 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, + 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, + 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, + 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, + 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, + 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, + 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, + 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, + 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, + 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 +]; + +function crypto_hashblocks_hl(hh, hl, m, n) { + var wh = new Int32Array(16), wl = new Int32Array(16), + bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, + bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7, + th, tl, i, j, h, l, a, b, c, d; + + var ah0 = hh[0], + ah1 = hh[1], + ah2 = hh[2], + ah3 = hh[3], + ah4 = hh[4], + ah5 = hh[5], + ah6 = hh[6], + ah7 = hh[7], + + al0 = hl[0], + al1 = hl[1], + al2 = hl[2], + al3 = hl[3], + al4 = hl[4], + al5 = hl[5], + al6 = hl[6], + al7 = hl[7]; + + var pos = 0; + while (n >= 128) { + for (i = 0; i < 16; i++) { + j = 8 * i + pos; + wh[i] = (m[j+0] << 24) | (m[j+1] << 16) | (m[j+2] << 8) | m[j+3]; + wl[i] = (m[j+4] << 24) | (m[j+5] << 16) | (m[j+6] << 8) | m[j+7]; + } + for (i = 0; i < 80; i++) { + bh0 = ah0; + bh1 = ah1; + bh2 = ah2; + bh3 = ah3; + bh4 = ah4; + bh5 = ah5; + bh6 = ah6; + bh7 = ah7; + + bl0 = al0; + bl1 = al1; + bl2 = al2; + bl3 = al3; + bl4 = al4; + bl5 = al5; + bl6 = al6; + bl7 = al7; + + // add + h = ah7; + l = al7; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + // Sigma1 + h = ((ah4 >>> 14) | (al4 << (32-14))) ^ ((ah4 >>> 18) | (al4 << (32-18))) ^ ((al4 >>> (41-32)) | (ah4 << (32-(41-32)))); + l = ((al4 >>> 14) | (ah4 << (32-14))) ^ ((al4 >>> 18) | (ah4 << (32-18))) ^ ((ah4 >>> (41-32)) | (al4 << (32-(41-32)))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // Ch + h = (ah4 & ah5) ^ (~ah4 & ah6); + l = (al4 & al5) ^ (~al4 & al6); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // K + h = K[i*2]; + l = K[i*2+1]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // w + h = wh[i%16]; + l = wl[i%16]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + th = c & 0xffff | d << 16; + tl = a & 0xffff | b << 16; + + // add + h = th; + l = tl; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + // Sigma0 + h = ((ah0 >>> 28) | (al0 << (32-28))) ^ ((al0 >>> (34-32)) | (ah0 << (32-(34-32)))) ^ ((al0 >>> (39-32)) | (ah0 << (32-(39-32)))); + l = ((al0 >>> 28) | (ah0 << (32-28))) ^ ((ah0 >>> (34-32)) | (al0 << (32-(34-32)))) ^ ((ah0 >>> (39-32)) | (al0 << (32-(39-32)))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // Maj + h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2); + l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + bh7 = (c & 0xffff) | (d << 16); + bl7 = (a & 0xffff) | (b << 16); + + // add + h = bh3; + l = bl3; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = th; + l = tl; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + bh3 = (c & 0xffff) | (d << 16); + bl3 = (a & 0xffff) | (b << 16); + + ah1 = bh0; + ah2 = bh1; + ah3 = bh2; + ah4 = bh3; + ah5 = bh4; + ah6 = bh5; + ah7 = bh6; + ah0 = bh7; + + al1 = bl0; + al2 = bl1; + al3 = bl2; + al4 = bl3; + al5 = bl4; + al6 = bl5; + al7 = bl6; + al0 = bl7; + + if (i%16 === 15) { + for (j = 0; j < 16; j++) { + // add + h = wh[j]; + l = wl[j]; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = wh[(j+9)%16]; + l = wl[(j+9)%16]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // sigma0 + th = wh[(j+1)%16]; + tl = wl[(j+1)%16]; + h = ((th >>> 1) | (tl << (32-1))) ^ ((th >>> 8) | (tl << (32-8))) ^ (th >>> 7); + l = ((tl >>> 1) | (th << (32-1))) ^ ((tl >>> 8) | (th << (32-8))) ^ ((tl >>> 7) | (th << (32-7))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // sigma1 + th = wh[(j+14)%16]; + tl = wl[(j+14)%16]; + h = ((th >>> 19) | (tl << (32-19))) ^ ((tl >>> (61-32)) | (th << (32-(61-32)))) ^ (th >>> 6); + l = ((tl >>> 19) | (th << (32-19))) ^ ((th >>> (61-32)) | (tl << (32-(61-32)))) ^ ((tl >>> 6) | (th << (32-6))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + wh[j] = (c & 0xffff) | (d << 16); + wl[j] = (a & 0xffff) | (b << 16); + } + } + } + + // add + h = ah0; + l = al0; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[0]; + l = hl[0]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[0] = ah0 = (c & 0xffff) | (d << 16); + hl[0] = al0 = (a & 0xffff) | (b << 16); + + h = ah1; + l = al1; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[1]; + l = hl[1]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[1] = ah1 = (c & 0xffff) | (d << 16); + hl[1] = al1 = (a & 0xffff) | (b << 16); + + h = ah2; + l = al2; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[2]; + l = hl[2]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[2] = ah2 = (c & 0xffff) | (d << 16); + hl[2] = al2 = (a & 0xffff) | (b << 16); + + h = ah3; + l = al3; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[3]; + l = hl[3]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[3] = ah3 = (c & 0xffff) | (d << 16); + hl[3] = al3 = (a & 0xffff) | (b << 16); + + h = ah4; + l = al4; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[4]; + l = hl[4]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[4] = ah4 = (c & 0xffff) | (d << 16); + hl[4] = al4 = (a & 0xffff) | (b << 16); + + h = ah5; + l = al5; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[5]; + l = hl[5]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[5] = ah5 = (c & 0xffff) | (d << 16); + hl[5] = al5 = (a & 0xffff) | (b << 16); + + h = ah6; + l = al6; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[6]; + l = hl[6]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[6] = ah6 = (c & 0xffff) | (d << 16); + hl[6] = al6 = (a & 0xffff) | (b << 16); + + h = ah7; + l = al7; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[7]; + l = hl[7]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[7] = ah7 = (c & 0xffff) | (d << 16); + hl[7] = al7 = (a & 0xffff) | (b << 16); + + pos += 128; + n -= 128; + } + + return n; +} + +function crypto_hash(out, m, n) { + var hh = new Int32Array(8), + hl = new Int32Array(8), + x = new Uint8Array(256), + i, b = n; + + hh[0] = 0x6a09e667; + hh[1] = 0xbb67ae85; + hh[2] = 0x3c6ef372; + hh[3] = 0xa54ff53a; + hh[4] = 0x510e527f; + hh[5] = 0x9b05688c; + hh[6] = 0x1f83d9ab; + hh[7] = 0x5be0cd19; + + hl[0] = 0xf3bcc908; + hl[1] = 0x84caa73b; + hl[2] = 0xfe94f82b; + hl[3] = 0x5f1d36f1; + hl[4] = 0xade682d1; + hl[5] = 0x2b3e6c1f; + hl[6] = 0xfb41bd6b; + hl[7] = 0x137e2179; + + crypto_hashblocks_hl(hh, hl, m, n); + n %= 128; + + for (i = 0; i < n; i++) x[i] = m[b-n+i]; + x[n] = 128; + + n = 256-128*(n<112?1:0); + x[n-9] = 0; + ts64(x, n-8, (b / 0x20000000) | 0, b << 3); + crypto_hashblocks_hl(hh, hl, x, n); + + for (i = 0; i < 8; i++) ts64(out, 8*i, hh[i], hl[i]); + + return 0; +} + +function add(p, q) { + var a = gf(), b = gf(), c = gf(), + d = gf(), e = gf(), f = gf(), + g = gf(), h = gf(), t = gf(); + + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} + +function cswap(p, q, b) { + var i; + for (i = 0; i < 4; i++) { + sel25519(p[i], q[i], b); + } +} + +function pack(r, p) { + var tx = gf(), ty = gf(), zi = gf(); + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} + +function scalarmult(p, q, s) { + var b, i; + set25519(p[0], gf0); + set25519(p[1], gf1); + set25519(p[2], gf1); + set25519(p[3], gf0); + for (i = 255; i >= 0; --i) { + b = (s[(i/8)|0] >> (i&7)) & 1; + cswap(p, q, b); + add(q, p); + add(p, p); + cswap(p, q, b); + } +} + +function scalarbase(p, s) { + var q = [gf(), gf(), gf(), gf()]; + set25519(q[0], X); + set25519(q[1], Y); + set25519(q[2], gf1); + M(q[3], X, Y); + scalarmult(p, q, s); +} + +function crypto_sign_keypair(pk, sk, seeded) { + var d = new Uint8Array(64); + var p = [gf(), gf(), gf(), gf()]; + var i; + + if (!seeded) randombytes(sk, 32); + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + scalarbase(p, d); + pack(pk, p); + + for (i = 0; i < 32; i++) sk[i+32] = pk[i]; + return 0; +} + +var L = new Float64Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]); + +function modL(r, x) { + var carry, i, j, k; + for (i = 63; i >= 32; --i) { + carry = 0; + for (j = i - 32, k = i - 12; j < k; ++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = (x[j] + 128) >> 8; + x[j] -= carry * 256; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + for (j = 0; j < 32; j++) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + for (j = 0; j < 32; j++) x[j] -= carry * L[j]; + for (i = 0; i < 32; i++) { + x[i+1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} + +function reduce(r) { + var x = new Float64Array(64), i; + for (i = 0; i < 64; i++) x[i] = r[i]; + for (i = 0; i < 64; i++) r[i] = 0; + modL(r, x); +} + +// Note: difference from C - smlen returned, not passed as argument. +function crypto_sign(sm, m, n, sk) { + var d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64); + var i, j, x = new Float64Array(64); + var p = [gf(), gf(), gf(), gf()]; + + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + var smlen = n + 64; + for (i = 0; i < n; i++) sm[64 + i] = m[i]; + for (i = 0; i < 32; i++) sm[32 + i] = d[32 + i]; + + crypto_hash(r, sm.subarray(32), n+32); + reduce(r); + scalarbase(p, r); + pack(sm, p); + + for (i = 32; i < 64; i++) sm[i] = sk[i]; + crypto_hash(h, sm, n + 64); + reduce(h); + + for (i = 0; i < 64; i++) x[i] = 0; + for (i = 0; i < 32; i++) x[i] = r[i]; + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + x[i+j] += h[i] * d[j]; + } + } + + modL(sm.subarray(32), x); + return smlen; +} + +function unpackneg(r, p) { + var t = gf(), chk = gf(), num = gf(), + den = gf(), den2 = gf(), den4 = gf(), + den6 = gf(); + + set25519(r[2], gf1); + unpack25519(r[1], p); + S(num, r[1]); + M(den, num, D); + Z(num, num, r[2]); + A(den, r[2], den); + + S(den2, den); + S(den4, den2); + M(den6, den4, den2); + M(t, den6, num); + M(t, t, den); + + pow2523(t, t); + M(t, t, num); + M(t, t, den); + M(t, t, den); + M(r[0], t, den); + + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) M(r[0], r[0], I); + + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) return -1; + + if (par25519(r[0]) === (p[31]>>7)) Z(r[0], gf0, r[0]); + + M(r[3], r[0], r[1]); + return 0; +} + +function crypto_sign_open(m, sm, n, pk) { + var i, mlen; + var t = new Uint8Array(32), h = new Uint8Array(64); + var p = [gf(), gf(), gf(), gf()], + q = [gf(), gf(), gf(), gf()]; + + mlen = -1; + if (n < 64) return -1; + + if (unpackneg(q, pk)) return -1; + + for (i = 0; i < n; i++) m[i] = sm[i]; + for (i = 0; i < 32; i++) m[i+32] = pk[i]; + crypto_hash(h, m, n); + reduce(h); + scalarmult(p, q, h); + + scalarbase(q, sm.subarray(32)); + add(p, q); + pack(t, p); + + n -= 64; + if (crypto_verify_32(sm, 0, t, 0)) { + for (i = 0; i < n; i++) m[i] = 0; + return -1; + } + + for (i = 0; i < n; i++) m[i] = sm[i + 64]; + mlen = n; + return mlen; +} + +var crypto_secretbox_KEYBYTES = 32, + crypto_secretbox_NONCEBYTES = 24, + crypto_secretbox_ZEROBYTES = 32, + crypto_secretbox_BOXZEROBYTES = 16, + crypto_scalarmult_BYTES = 32, + crypto_scalarmult_SCALARBYTES = 32, + crypto_box_PUBLICKEYBYTES = 32, + crypto_box_SECRETKEYBYTES = 32, + crypto_box_BEFORENMBYTES = 32, + crypto_box_NONCEBYTES = crypto_secretbox_NONCEBYTES, + crypto_box_ZEROBYTES = crypto_secretbox_ZEROBYTES, + crypto_box_BOXZEROBYTES = crypto_secretbox_BOXZEROBYTES, + crypto_sign_BYTES = 64, + crypto_sign_PUBLICKEYBYTES = 32, + crypto_sign_SECRETKEYBYTES = 64, + crypto_sign_SEEDBYTES = 32, + crypto_hash_BYTES = 64; + +nacl.lowlevel = { + crypto_core_hsalsa20: crypto_core_hsalsa20, + crypto_stream_xor: crypto_stream_xor, + crypto_stream: crypto_stream, + crypto_stream_salsa20_xor: crypto_stream_salsa20_xor, + crypto_stream_salsa20: crypto_stream_salsa20, + crypto_onetimeauth: crypto_onetimeauth, + crypto_onetimeauth_verify: crypto_onetimeauth_verify, + crypto_verify_16: crypto_verify_16, + crypto_verify_32: crypto_verify_32, + crypto_secretbox: crypto_secretbox, + crypto_secretbox_open: crypto_secretbox_open, + crypto_scalarmult: crypto_scalarmult, + crypto_scalarmult_base: crypto_scalarmult_base, + crypto_box_beforenm: crypto_box_beforenm, + crypto_box_afternm: crypto_box_afternm, + crypto_box: crypto_box, + crypto_box_open: crypto_box_open, + crypto_box_keypair: crypto_box_keypair, + crypto_hash: crypto_hash, + crypto_sign: crypto_sign, + crypto_sign_keypair: crypto_sign_keypair, + crypto_sign_open: crypto_sign_open, + + crypto_secretbox_KEYBYTES: crypto_secretbox_KEYBYTES, + crypto_secretbox_NONCEBYTES: crypto_secretbox_NONCEBYTES, + crypto_secretbox_ZEROBYTES: crypto_secretbox_ZEROBYTES, + crypto_secretbox_BOXZEROBYTES: crypto_secretbox_BOXZEROBYTES, + crypto_scalarmult_BYTES: crypto_scalarmult_BYTES, + crypto_scalarmult_SCALARBYTES: crypto_scalarmult_SCALARBYTES, + crypto_box_PUBLICKEYBYTES: crypto_box_PUBLICKEYBYTES, + crypto_box_SECRETKEYBYTES: crypto_box_SECRETKEYBYTES, + crypto_box_BEFORENMBYTES: crypto_box_BEFORENMBYTES, + crypto_box_NONCEBYTES: crypto_box_NONCEBYTES, + crypto_box_ZEROBYTES: crypto_box_ZEROBYTES, + crypto_box_BOXZEROBYTES: crypto_box_BOXZEROBYTES, + crypto_sign_BYTES: crypto_sign_BYTES, + crypto_sign_PUBLICKEYBYTES: crypto_sign_PUBLICKEYBYTES, + crypto_sign_SECRETKEYBYTES: crypto_sign_SECRETKEYBYTES, + crypto_sign_SEEDBYTES: crypto_sign_SEEDBYTES, + crypto_hash_BYTES: crypto_hash_BYTES +}; + +/* High-level API */ + +function checkLengths(k, n) { + if (k.length !== crypto_secretbox_KEYBYTES) throw new Error('bad key size'); + if (n.length !== crypto_secretbox_NONCEBYTES) throw new Error('bad nonce size'); +} + +function checkBoxLengths(pk, sk) { + if (pk.length !== crypto_box_PUBLICKEYBYTES) throw new Error('bad public key size'); + if (sk.length !== crypto_box_SECRETKEYBYTES) throw new Error('bad secret key size'); +} + +function checkArrayTypes() { + var t, i; + for (i = 0; i < arguments.length; i++) { + if ((t = Object.prototype.toString.call(arguments[i])) !== '[object Uint8Array]') + throw new TypeError('unexpected type ' + t + ', use Uint8Array'); + } +} + +function cleanup(arr) { + for (var i = 0; i < arr.length; i++) arr[i] = 0; +} + +// TODO: Completely remove this in v0.15. +if (!nacl.util) { + nacl.util = {}; + nacl.util.decodeUTF8 = nacl.util.encodeUTF8 = nacl.util.encodeBase64 = nacl.util.decodeBase64 = function() { + throw new Error('nacl.util moved into separate package: https://github.com/dchest/tweetnacl-util-js'); + }; +} + +nacl.randomBytes = function(n) { + var b = new Uint8Array(n); + randombytes(b, n); + return b; +}; + +nacl.secretbox = function(msg, nonce, key) { + checkArrayTypes(msg, nonce, key); + checkLengths(key, nonce); + var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length); + var c = new Uint8Array(m.length); + for (var i = 0; i < msg.length; i++) m[i+crypto_secretbox_ZEROBYTES] = msg[i]; + crypto_secretbox(c, m, m.length, nonce, key); + return c.subarray(crypto_secretbox_BOXZEROBYTES); +}; + +nacl.secretbox.open = function(box, nonce, key) { + checkArrayTypes(box, nonce, key); + checkLengths(key, nonce); + var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length); + var m = new Uint8Array(c.length); + for (var i = 0; i < box.length; i++) c[i+crypto_secretbox_BOXZEROBYTES] = box[i]; + if (c.length < 32) return false; + if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) return false; + return m.subarray(crypto_secretbox_ZEROBYTES); +}; + +nacl.secretbox.keyLength = crypto_secretbox_KEYBYTES; +nacl.secretbox.nonceLength = crypto_secretbox_NONCEBYTES; +nacl.secretbox.overheadLength = crypto_secretbox_BOXZEROBYTES; + +nacl.scalarMult = function(n, p) { + checkArrayTypes(n, p); + if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size'); + if (p.length !== crypto_scalarmult_BYTES) throw new Error('bad p size'); + var q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult(q, n, p); + return q; +}; + +nacl.scalarMult.base = function(n) { + checkArrayTypes(n); + if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size'); + var q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult_base(q, n); + return q; +}; + +nacl.scalarMult.scalarLength = crypto_scalarmult_SCALARBYTES; +nacl.scalarMult.groupElementLength = crypto_scalarmult_BYTES; + +nacl.box = function(msg, nonce, publicKey, secretKey) { + var k = nacl.box.before(publicKey, secretKey); + return nacl.secretbox(msg, nonce, k); +}; + +nacl.box.before = function(publicKey, secretKey) { + checkArrayTypes(publicKey, secretKey); + checkBoxLengths(publicKey, secretKey); + var k = new Uint8Array(crypto_box_BEFORENMBYTES); + crypto_box_beforenm(k, publicKey, secretKey); + return k; +}; + +nacl.box.after = nacl.secretbox; + +nacl.box.open = function(msg, nonce, publicKey, secretKey) { + var k = nacl.box.before(publicKey, secretKey); + return nacl.secretbox.open(msg, nonce, k); +}; + +nacl.box.open.after = nacl.secretbox.open; + +nacl.box.keyPair = function() { + var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_box_SECRETKEYBYTES); + crypto_box_keypair(pk, sk); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.box.keyPair.fromSecretKey = function(secretKey) { + checkArrayTypes(secretKey); + if (secretKey.length !== crypto_box_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); + crypto_scalarmult_base(pk, secretKey); + return {publicKey: pk, secretKey: new Uint8Array(secretKey)}; +}; + +nacl.box.publicKeyLength = crypto_box_PUBLICKEYBYTES; +nacl.box.secretKeyLength = crypto_box_SECRETKEYBYTES; +nacl.box.sharedKeyLength = crypto_box_BEFORENMBYTES; +nacl.box.nonceLength = crypto_box_NONCEBYTES; +nacl.box.overheadLength = nacl.secretbox.overheadLength; + +nacl.sign = function(msg, secretKey) { + checkArrayTypes(msg, secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var signedMsg = new Uint8Array(crypto_sign_BYTES+msg.length); + crypto_sign(signedMsg, msg, msg.length, secretKey); + return signedMsg; +}; + +nacl.sign.open = function(signedMsg, publicKey) { + if (arguments.length !== 2) + throw new Error('nacl.sign.open accepts 2 arguments; did you mean to use nacl.sign.detached.verify?'); + checkArrayTypes(signedMsg, publicKey); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error('bad public key size'); + var tmp = new Uint8Array(signedMsg.length); + var mlen = crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey); + if (mlen < 0) return null; + var m = new Uint8Array(mlen); + for (var i = 0; i < m.length; i++) m[i] = tmp[i]; + return m; +}; + +nacl.sign.detached = function(msg, secretKey) { + var signedMsg = nacl.sign(msg, secretKey); + var sig = new Uint8Array(crypto_sign_BYTES); + for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i]; + return sig; +}; + +nacl.sign.detached.verify = function(msg, sig, publicKey) { + checkArrayTypes(msg, sig, publicKey); + if (sig.length !== crypto_sign_BYTES) + throw new Error('bad signature size'); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error('bad public key size'); + var sm = new Uint8Array(crypto_sign_BYTES + msg.length); + var m = new Uint8Array(crypto_sign_BYTES + msg.length); + var i; + for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i]; + for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i]; + return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0); +}; + +nacl.sign.keyPair = function() { + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + crypto_sign_keypair(pk, sk); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.sign.keyPair.fromSecretKey = function(secretKey) { + checkArrayTypes(secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + for (var i = 0; i < pk.length; i++) pk[i] = secretKey[32+i]; + return {publicKey: pk, secretKey: new Uint8Array(secretKey)}; +}; + +nacl.sign.keyPair.fromSeed = function(seed) { + checkArrayTypes(seed); + if (seed.length !== crypto_sign_SEEDBYTES) + throw new Error('bad seed size'); + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + for (var i = 0; i < 32; i++) sk[i] = seed[i]; + crypto_sign_keypair(pk, sk, true); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.sign.publicKeyLength = crypto_sign_PUBLICKEYBYTES; +nacl.sign.secretKeyLength = crypto_sign_SECRETKEYBYTES; +nacl.sign.seedLength = crypto_sign_SEEDBYTES; +nacl.sign.signatureLength = crypto_sign_BYTES; + +nacl.hash = function(msg) { + checkArrayTypes(msg); + var h = new Uint8Array(crypto_hash_BYTES); + crypto_hash(h, msg, msg.length); + return h; +}; + +nacl.hash.hashLength = crypto_hash_BYTES; + +nacl.verify = function(x, y) { + checkArrayTypes(x, y); + // Zero length arguments are considered not equal. + if (x.length === 0 || y.length === 0) return false; + if (x.length !== y.length) return false; + return (vn(x, 0, y, 0, x.length) === 0) ? true : false; +}; + +nacl.setPRNG = function(fn) { + randombytes = fn; +}; + +(function() { + // Initialize PRNG if environment provides CSPRNG. + // If not, methods calling randombytes will throw. + var crypto = typeof self !== 'undefined' ? (self.crypto || self.msCrypto) : null; + if (crypto && crypto.getRandomValues) { + // Browsers. + var QUOTA = 65536; + nacl.setPRNG(function(x, n) { + var i, v = new Uint8Array(n); + for (i = 0; i < n; i += QUOTA) { + crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA))); + } + for (i = 0; i < n; i++) x[i] = v[i]; + cleanup(v); + }); + } else if (true) { + // Node.js. + crypto = __webpack_require__(417); + if (crypto && crypto.randomBytes) { + nacl.setPRNG(function(x, n) { + var i, v = crypto.randomBytes(n); + for (i = 0; i < n; i++) x[i] = v[i]; + cleanup(v); + }); + } + } +})(); + +})( true && module.exports ? module.exports : (self.nacl = self.nacl || {})); + + +/***/ }), + +/***/ 211: +/***/ (function(module) { + +module.exports = require("https"); + +/***/ }), + +/***/ 215: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; +/* eslint-disable node/no-deprecated-api */ + + + +var buffer = __webpack_require__(293) +var Buffer = buffer.Buffer + +var safer = {} + +var key + +for (key in buffer) { + if (!buffer.hasOwnProperty(key)) continue + if (key === 'SlowBuffer' || key === 'Buffer') continue + safer[key] = buffer[key] +} + +var Safer = safer.Buffer = {} +for (key in Buffer) { + if (!Buffer.hasOwnProperty(key)) continue + if (key === 'allocUnsafe' || key === 'allocUnsafeSlow') continue + Safer[key] = Buffer[key] +} + +safer.Buffer.prototype = Buffer.prototype + +if (!Safer.from || Safer.from === Uint8Array.from) { + Safer.from = function (value, encodingOrOffset, length) { + if (typeof value === 'number') { + throw new TypeError('The "value" argument must not be of type number. Received type ' + typeof value) + } + if (value && typeof value.length === 'undefined') { + throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ' + typeof value) + } + return Buffer(value, encodingOrOffset, length) + } +} + +if (!Safer.alloc) { + Safer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size) + } + if (size < 0 || size >= 2 * (1 << 30)) { + throw new RangeError('The value "' + size + '" is invalid for option "size"') + } + var buf = Buffer(size) + if (!fill || fill.length === 0) { + buf.fill(0) + } else if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + return buf + } +} + +if (!safer.kStringMaxLength) { + try { + safer.kStringMaxLength = process.binding('buffer').kStringMaxLength + } catch (e) { + // we can't determine kStringMaxLength in environments where process.binding + // is unsupported, so let's not set it + } +} + +if (!safer.constants) { + safer.constants = { + MAX_LENGTH: safer.kMaxLength + } + if (safer.kStringMaxLength) { + safer.constants.MAX_STRING_LENGTH = safer.kStringMaxLength + } +} + +module.exports = safer + + +/***/ }), + +/***/ 216: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var ERR_INVALID_OPT_VALUE = __webpack_require__(563).codes.ERR_INVALID_OPT_VALUE; + +function highWaterMarkFrom(options, isDuplex, duplexKey) { + return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null; +} + +function getHighWaterMark(state, options, duplexKey, isDuplex) { + var hwm = highWaterMarkFrom(options, isDuplex, duplexKey); + + if (hwm != null) { + if (!(isFinite(hwm) && Math.floor(hwm) === hwm) || hwm < 0) { + var name = isDuplex ? duplexKey : 'highWaterMark'; + throw new ERR_INVALID_OPT_VALUE(name, hwm); + } + + return Math.floor(hwm); + } // Default value + + + return state.objectMode ? 16 : 16 * 1024; +} + +module.exports = { + getHighWaterMark: getHighWaterMark +}; + +/***/ }), + +/***/ 226: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +module.exports = Readable; +/**/ + +var Duplex; +/**/ + +Readable.ReadableState = ReadableState; +/**/ + +var EE = __webpack_require__(614).EventEmitter; + +var EElistenerCount = function EElistenerCount(emitter, type) { + return emitter.listeners(type).length; +}; +/**/ + +/**/ + + +var Stream = __webpack_require__(427); +/**/ + + +var Buffer = __webpack_require__(293).Buffer; + +var OurUint8Array = global.Uint8Array || function () {}; + +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} + +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} +/**/ + + +var debugUtil = __webpack_require__(669); + +var debug; + +if (debugUtil && debugUtil.debuglog) { + debug = debugUtil.debuglog('stream'); +} else { + debug = function debug() {}; +} +/**/ + + +var BufferList = __webpack_require__(896); + +var destroyImpl = __webpack_require__(232); + +var _require = __webpack_require__(216), + getHighWaterMark = _require.getHighWaterMark; + +var _require$codes = __webpack_require__(563).codes, + ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, + ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF, + ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, + ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; // Lazy loaded to improve the startup performance. + + +var StringDecoder; +var createReadableStreamAsyncIterator; +var from; + +__webpack_require__(689)(Readable, Stream); + +var errorOrDestroy = destroyImpl.errorOrDestroy; +var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; + +function prependListener(emitter, event, fn) { + // Sadly this is not cacheable as some libraries bundle their own + // event emitter implementation with them. + if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any + // userland ones. NEVER DO THIS. This is here only because this code needs + // to continue to work with older versions of Node.js that do not include + // the prependListener() method. The goal is to eventually remove this hack. + + if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; +} + +function ReadableState(options, stream, isDuplex) { + Duplex = Duplex || __webpack_require__(831); + options = options || {}; // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + + if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away + + this.objectMode = !!options.objectMode; + if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + + this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); // A linked list is used to store data chunks instead of an array because the + // linked list can remove elements from the beginning faster than + // array.shift() + + this.buffer = new BufferList(); + this.length = 0; + this.pipes = null; + this.pipesCount = 0; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted + // immediately, or on a later tick. We set this to true at first, because + // any actions that shouldn't happen until "later" should generally also + // not happen before the first read call. + + this.sync = true; // whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + this.resumeScheduled = false; + this.paused = true; // Should close be emitted on destroy. Defaults to true. + + this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'end' (and potentially 'finish') + + this.autoDestroy = !!options.autoDestroy; // has it been destroyed + + this.destroyed = false; // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + + this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s + + this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled + + this.readingMore = false; + this.decoder = null; + this.encoding = null; + + if (options.encoding) { + if (!StringDecoder) StringDecoder = __webpack_require__(674).StringDecoder; + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; + } +} + +function Readable(options) { + Duplex = Duplex || __webpack_require__(831); + if (!(this instanceof Readable)) return new Readable(options); // Checking for a Stream.Duplex instance is faster here instead of inside + // the ReadableState constructor, at least with V8 6.5 + + var isDuplex = this instanceof Duplex; + this._readableState = new ReadableState(options, this, isDuplex); // legacy + + this.readable = true; + + if (options) { + if (typeof options.read === 'function') this._read = options.read; + if (typeof options.destroy === 'function') this._destroy = options.destroy; + } + + Stream.call(this); +} + +Object.defineProperty(Readable.prototype, 'destroyed', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + if (this._readableState === undefined) { + return false; + } + + return this._readableState.destroyed; + }, + set: function set(value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._readableState) { + return; + } // backward compatibility, the user is explicitly + // managing destroyed + + + this._readableState.destroyed = value; + } +}); +Readable.prototype.destroy = destroyImpl.destroy; +Readable.prototype._undestroy = destroyImpl.undestroy; + +Readable.prototype._destroy = function (err, cb) { + cb(err); +}; // Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. + + +Readable.prototype.push = function (chunk, encoding) { + var state = this._readableState; + var skipChunkCheck; + + if (!state.objectMode) { + if (typeof chunk === 'string') { + encoding = encoding || state.defaultEncoding; + + if (encoding !== state.encoding) { + chunk = Buffer.from(chunk, encoding); + encoding = ''; + } + + skipChunkCheck = true; + } + } else { + skipChunkCheck = true; + } + + return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); +}; // Unshift should *always* be something directly out of read() + + +Readable.prototype.unshift = function (chunk) { + return readableAddChunk(this, chunk, null, true, false); +}; + +function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { + debug('readableAddChunk', chunk); + var state = stream._readableState; + + if (chunk === null) { + state.reading = false; + onEofChunk(stream, state); + } else { + var er; + if (!skipChunkCheck) er = chunkInvalid(state, chunk); + + if (er) { + errorOrDestroy(stream, er); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (addToFront) { + if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true); + } else if (state.ended) { + errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); + } else if (state.destroyed) { + return false; + } else { + state.reading = false; + + if (state.decoder && !encoding) { + chunk = state.decoder.write(chunk); + if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); + } else { + addChunk(stream, state, chunk, false); + } + } + } else if (!addToFront) { + state.reading = false; + maybeReadMore(stream, state); + } + } // We can push more data if we are below the highWaterMark. + // Also, if we have no data yet, we can stand some more bytes. + // This is to work around cases where hwm=0, such as the repl. + + + return !state.ended && (state.length < state.highWaterMark || state.length === 0); +} + +function addChunk(stream, state, chunk, addToFront) { + if (state.flowing && state.length === 0 && !state.sync) { + state.awaitDrain = 0; + stream.emit('data', chunk); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); + if (state.needReadable) emitReadable(stream); + } + + maybeReadMore(stream, state); +} + +function chunkInvalid(state, chunk) { + var er; + + if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk); + } + + return er; +} + +Readable.prototype.isPaused = function () { + return this._readableState.flowing === false; +}; // backwards compatibility. + + +Readable.prototype.setEncoding = function (enc) { + if (!StringDecoder) StringDecoder = __webpack_require__(674).StringDecoder; + var decoder = new StringDecoder(enc); + this._readableState.decoder = decoder; // If setEncoding(null), decoder.encoding equals utf8 + + this._readableState.encoding = this._readableState.decoder.encoding; // Iterate over current buffer to convert already stored Buffers: + + var p = this._readableState.buffer.head; + var content = ''; + + while (p !== null) { + content += decoder.write(p.data); + p = p.next; + } + + this._readableState.buffer.clear(); + + if (content !== '') this._readableState.buffer.push(content); + this._readableState.length = content.length; + return this; +}; // Don't raise the hwm > 1GB + + +var MAX_HWM = 0x40000000; + +function computeNewHighWaterMark(n) { + if (n >= MAX_HWM) { + // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE. + n = MAX_HWM; + } else { + // Get the next highest power of 2 to prevent increasing hwm excessively in + // tiny amounts + n--; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + n++; + } + + return n; +} // This function is designed to be inlinable, so please take care when making +// changes to the function body. + + +function howMuchToRead(n, state) { + if (n <= 0 || state.length === 0 && state.ended) return 0; + if (state.objectMode) return 1; + + if (n !== n) { + // Only flow one buffer at a time + if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; + } // If we're asking for more than the current hwm, then raise the hwm. + + + if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); + if (n <= state.length) return n; // Don't have enough + + if (!state.ended) { + state.needReadable = true; + return 0; + } + + return state.length; +} // you can override either this method, or the async _read(n) below. + + +Readable.prototype.read = function (n) { + debug('read', n); + n = parseInt(n, 10); + var state = this._readableState; + var nOrig = n; + if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + + if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) { + debug('read: emitReadable', state.length, state.ended); + if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); + return null; + } + + n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up. + + if (n === 0 && state.ended) { + if (state.length === 0) endReadable(this); + return null; + } // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. + // if we need a readable event, then we need to do some reading. + + + var doRead = state.needReadable; + debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some + + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + debug('length less than watermark', doRead); + } // however, if we've ended, then there's no point, and if we're already + // reading, then it's unnecessary. + + + if (state.ended || state.reading) { + doRead = false; + debug('reading or ended', doRead); + } else if (doRead) { + debug('do read'); + state.reading = true; + state.sync = true; // if the length is currently zero, then we *need* a readable event. + + if (state.length === 0) state.needReadable = true; // call internal read method + + this._read(state.highWaterMark); + + state.sync = false; // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + + if (!state.reading) n = howMuchToRead(nOrig, state); + } + + var ret; + if (n > 0) ret = fromList(n, state);else ret = null; + + if (ret === null) { + state.needReadable = state.length <= state.highWaterMark; + n = 0; + } else { + state.length -= n; + state.awaitDrain = 0; + } + + if (state.length === 0) { + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick. + + if (nOrig !== n && state.ended) endReadable(this); + } + + if (ret !== null) this.emit('data', ret); + return ret; +}; + +function onEofChunk(stream, state) { + debug('onEofChunk'); + if (state.ended) return; + + if (state.decoder) { + var chunk = state.decoder.end(); + + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; + } + } + + state.ended = true; + + if (state.sync) { + // if we are sync, wait until next tick to emit the data. + // Otherwise we risk emitting data in the flow() + // the readable code triggers during a read() call + emitReadable(stream); + } else { + // emit 'readable' now to make sure it gets picked up. + state.needReadable = false; + + if (!state.emittedReadable) { + state.emittedReadable = true; + emitReadable_(stream); + } + } +} // Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. + + +function emitReadable(stream) { + var state = stream._readableState; + debug('emitReadable', state.needReadable, state.emittedReadable); + state.needReadable = false; + + if (!state.emittedReadable) { + debug('emitReadable', state.flowing); + state.emittedReadable = true; + process.nextTick(emitReadable_, stream); + } +} + +function emitReadable_(stream) { + var state = stream._readableState; + debug('emitReadable_', state.destroyed, state.length, state.ended); + + if (!state.destroyed && (state.length || state.ended)) { + stream.emit('readable'); + state.emittedReadable = false; + } // The stream needs another readable event if + // 1. It is not flowing, as the flow mechanism will take + // care of it. + // 2. It is not ended. + // 3. It is below the highWaterMark, so we can schedule + // another readable later. + + + state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark; + flow(stream); +} // at this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. + + +function maybeReadMore(stream, state) { + if (!state.readingMore) { + state.readingMore = true; + process.nextTick(maybeReadMore_, stream, state); + } +} + +function maybeReadMore_(stream, state) { + // Attempt to read more data if we should. + // + // The conditions for reading more data are (one of): + // - Not enough data buffered (state.length < state.highWaterMark). The loop + // is responsible for filling the buffer with enough data if such data + // is available. If highWaterMark is 0 and we are not in the flowing mode + // we should _not_ attempt to buffer any extra data. We'll get more data + // when the stream consumer calls read() instead. + // - No data in the buffer, and the stream is in flowing mode. In this mode + // the loop below is responsible for ensuring read() is called. Failing to + // call read here would abort the flow and there's no other mechanism for + // continuing the flow if the stream consumer has just subscribed to the + // 'data' event. + // + // In addition to the above conditions to keep reading data, the following + // conditions prevent the data from being read: + // - The stream has ended (state.ended). + // - There is already a pending 'read' operation (state.reading). This is a + // case where the the stream has called the implementation defined _read() + // method, but they are processing the call asynchronously and have _not_ + // called push() with new data. In this case we skip performing more + // read()s. The execution ends in this method again after the _read() ends + // up calling push() with more data. + while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) { + var len = state.length; + debug('maybeReadMore read 0'); + stream.read(0); + if (len === state.length) // didn't get any data, stop spinning. + break; + } + + state.readingMore = false; +} // abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. + + +Readable.prototype._read = function (n) { + errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()')); +}; + +Readable.prototype.pipe = function (dest, pipeOpts) { + var src = this; + var state = this._readableState; + + switch (state.pipesCount) { + case 0: + state.pipes = dest; + break; + + case 1: + state.pipes = [state.pipes, dest]; + break; + + default: + state.pipes.push(dest); + break; + } + + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); + var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; + var endFn = doEnd ? onend : unpipe; + if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn); + dest.on('unpipe', onunpipe); + + function onunpipe(readable, unpipeInfo) { + debug('onunpipe'); + + if (readable === src) { + if (unpipeInfo && unpipeInfo.hasUnpiped === false) { + unpipeInfo.hasUnpiped = true; + cleanup(); + } + } + } + + function onend() { + debug('onend'); + dest.end(); + } // when the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + + + var ondrain = pipeOnDrain(src); + dest.on('drain', ondrain); + var cleanedUp = false; + + function cleanup() { + debug('cleanup'); // cleanup event handlers once the pipe is broken + + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + dest.removeListener('drain', ondrain); + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', unpipe); + src.removeListener('data', ondata); + cleanedUp = true; // if the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + + if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); + } + + src.on('data', ondata); + + function ondata(chunk) { + debug('ondata'); + var ret = dest.write(chunk); + debug('dest.write', ret); + + if (ret === false) { + // If the user unpiped during `dest.write()`, it is possible + // to get stuck in a permanently paused state if that write + // also returned false. + // => Check whether `dest` is still a piping destination. + if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { + debug('false write response, pause', state.awaitDrain); + state.awaitDrain++; + } + + src.pause(); + } + } // if the dest has an error, then stop piping into it. + // however, don't suppress the throwing behavior for this. + + + function onerror(er) { + debug('onerror', er); + unpipe(); + dest.removeListener('error', onerror); + if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er); + } // Make sure our error handler is attached before userland ones. + + + prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once. + + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); + } + + dest.once('close', onclose); + + function onfinish() { + debug('onfinish'); + dest.removeListener('close', onclose); + unpipe(); + } + + dest.once('finish', onfinish); + + function unpipe() { + debug('unpipe'); + src.unpipe(dest); + } // tell the dest that it's being piped to + + + dest.emit('pipe', src); // start the flow if it hasn't been started already. + + if (!state.flowing) { + debug('pipe resume'); + src.resume(); + } + + return dest; +}; + +function pipeOnDrain(src) { + return function pipeOnDrainFunctionResult() { + var state = src._readableState; + debug('pipeOnDrain', state.awaitDrain); + if (state.awaitDrain) state.awaitDrain--; + + if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { + state.flowing = true; + flow(src); + } + }; +} + +Readable.prototype.unpipe = function (dest) { + var state = this._readableState; + var unpipeInfo = { + hasUnpiped: false + }; // if we're not piping anywhere, then do nothing. + + if (state.pipesCount === 0) return this; // just one destination. most common case. + + if (state.pipesCount === 1) { + // passed in one, but it's not the right one. + if (dest && dest !== state.pipes) return this; + if (!dest) dest = state.pipes; // got a match. + + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) dest.emit('unpipe', this, unpipeInfo); + return this; + } // slow case. multiple pipe destinations. + + + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + + for (var i = 0; i < len; i++) { + dests[i].emit('unpipe', this, { + hasUnpiped: false + }); + } + + return this; + } // try to find the right one. + + + var index = indexOf(state.pipes, dest); + if (index === -1) return this; + state.pipes.splice(index, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) state.pipes = state.pipes[0]; + dest.emit('unpipe', this, unpipeInfo); + return this; +}; // set up data events if they are asked for +// Ensure readable listeners eventually get something + + +Readable.prototype.on = function (ev, fn) { + var res = Stream.prototype.on.call(this, ev, fn); + var state = this._readableState; + + if (ev === 'data') { + // update readableListening so that resume() may be a no-op + // a few lines down. This is needed to support once('readable'). + state.readableListening = this.listenerCount('readable') > 0; // Try start flowing on next tick if stream isn't explicitly paused + + if (state.flowing !== false) this.resume(); + } else if (ev === 'readable') { + if (!state.endEmitted && !state.readableListening) { + state.readableListening = state.needReadable = true; + state.flowing = false; + state.emittedReadable = false; + debug('on readable', state.length, state.reading); + + if (state.length) { + emitReadable(this); + } else if (!state.reading) { + process.nextTick(nReadingNextTick, this); + } + } + } + + return res; +}; + +Readable.prototype.addListener = Readable.prototype.on; + +Readable.prototype.removeListener = function (ev, fn) { + var res = Stream.prototype.removeListener.call(this, ev, fn); + + if (ev === 'readable') { + // We need to check if there is someone still listening to + // readable and reset the state. However this needs to happen + // after readable has been emitted but before I/O (nextTick) to + // support once('readable', fn) cycles. This means that calling + // resume within the same tick will have no + // effect. + process.nextTick(updateReadableListening, this); + } + + return res; +}; + +Readable.prototype.removeAllListeners = function (ev) { + var res = Stream.prototype.removeAllListeners.apply(this, arguments); + + if (ev === 'readable' || ev === undefined) { + // We need to check if there is someone still listening to + // readable and reset the state. However this needs to happen + // after readable has been emitted but before I/O (nextTick) to + // support once('readable', fn) cycles. This means that calling + // resume within the same tick will have no + // effect. + process.nextTick(updateReadableListening, this); + } + + return res; +}; + +function updateReadableListening(self) { + var state = self._readableState; + state.readableListening = self.listenerCount('readable') > 0; + + if (state.resumeScheduled && !state.paused) { + // flowing needs to be set to true now, otherwise + // the upcoming resume will not flow. + state.flowing = true; // crude way to check if we should resume + } else if (self.listenerCount('data') > 0) { + self.resume(); + } +} + +function nReadingNextTick(self) { + debug('readable nexttick read 0'); + self.read(0); +} // pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. + + +Readable.prototype.resume = function () { + var state = this._readableState; + + if (!state.flowing) { + debug('resume'); // we flow only if there is no one listening + // for readable, but we still have to call + // resume() + + state.flowing = !state.readableListening; + resume(this, state); + } + + state.paused = false; + return this; +}; + +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + process.nextTick(resume_, stream, state); + } +} + +function resume_(stream, state) { + debug('resume', state.reading); + + if (!state.reading) { + stream.read(0); + } + + state.resumeScheduled = false; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) stream.read(0); +} + +Readable.prototype.pause = function () { + debug('call pause flowing=%j', this._readableState.flowing); + + if (this._readableState.flowing !== false) { + debug('pause'); + this._readableState.flowing = false; + this.emit('pause'); + } + + this._readableState.paused = true; + return this; +}; + +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + + while (state.flowing && stream.read() !== null) { + ; + } +} // wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. + + +Readable.prototype.wrap = function (stream) { + var _this = this; + + var state = this._readableState; + var paused = false; + stream.on('end', function () { + debug('wrapped end'); + + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) _this.push(chunk); + } + + _this.push(null); + }); + stream.on('data', function (chunk) { + debug('wrapped data'); + if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode + + if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; + + var ret = _this.push(chunk); + + if (!ret) { + paused = true; + stream.pause(); + } + }); // proxy all the other methods. + // important when wrapping filters and duplexes. + + for (var i in stream) { + if (this[i] === undefined && typeof stream[i] === 'function') { + this[i] = function methodWrap(method) { + return function methodWrapReturnFunction() { + return stream[method].apply(stream, arguments); + }; + }(i); + } + } // proxy certain important events. + + + for (var n = 0; n < kProxyEvents.length; n++) { + stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); + } // when we try to consume some more bytes, simply unpause the + // underlying stream. + + + this._read = function (n) { + debug('wrapped _read', n); + + if (paused) { + paused = false; + stream.resume(); + } + }; + + return this; +}; + +if (typeof Symbol === 'function') { + Readable.prototype[Symbol.asyncIterator] = function () { + if (createReadableStreamAsyncIterator === undefined) { + createReadableStreamAsyncIterator = __webpack_require__(46); + } + + return createReadableStreamAsyncIterator(this); + }; +} + +Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._readableState.highWaterMark; + } +}); +Object.defineProperty(Readable.prototype, 'readableBuffer', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._readableState && this._readableState.buffer; + } +}); +Object.defineProperty(Readable.prototype, 'readableFlowing', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._readableState.flowing; + }, + set: function set(state) { + if (this._readableState) { + this._readableState.flowing = state; + } + } +}); // exposed for testing purposes only. + +Readable._fromList = fromList; +Object.defineProperty(Readable.prototype, 'readableLength', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._readableState.length; + } +}); // Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. + +function fromList(n, state) { + // nothing buffered + if (state.length === 0) return null; + var ret; + if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { + // read it all, truncate the list + if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length); + state.buffer.clear(); + } else { + // read part of list + ret = state.buffer.consume(n, state.decoder); + } + return ret; +} + +function endReadable(stream) { + var state = stream._readableState; + debug('endReadable', state.endEmitted); + + if (!state.endEmitted) { + state.ended = true; + process.nextTick(endReadableNT, state, stream); + } +} + +function endReadableNT(state, stream) { + debug('endReadableNT', state.endEmitted, state.length); // Check that we didn't get one last unshift. + + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); + + if (state.autoDestroy) { + // In case of duplex streams we need a way to detect + // if the writable side is ready for autoDestroy as well + var wState = stream._writableState; + + if (!wState || wState.autoDestroy && wState.finished) { + stream.destroy(); + } + } + } +} + +if (typeof Symbol === 'function') { + Readable.from = function (iterable, opts) { + if (from === undefined) { + from = __webpack_require__(176); + } + + return from(Readable, iterable, opts); + }; +} + +function indexOf(xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; + } + + return -1; +} + +/***/ }), + +/***/ 232: +/***/ (function(module) { + +"use strict"; + // undocumented cb() API, needed for core, not for public API + +function destroy(err, cb) { + var _this = this; + + var readableDestroyed = this._readableState && this._readableState.destroyed; + var writableDestroyed = this._writableState && this._writableState.destroyed; + + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err) { + if (!this._writableState) { + process.nextTick(emitErrorNT, this, err); + } else if (!this._writableState.errorEmitted) { + this._writableState.errorEmitted = true; + process.nextTick(emitErrorNT, this, err); + } + } + + return this; + } // we set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks + + + if (this._readableState) { + this._readableState.destroyed = true; + } // if this is a duplex stream mark the writable part as destroyed as well + + + if (this._writableState) { + this._writableState.destroyed = true; + } + + this._destroy(err || null, function (err) { + if (!cb && err) { + if (!_this._writableState) { + process.nextTick(emitErrorAndCloseNT, _this, err); + } else if (!_this._writableState.errorEmitted) { + _this._writableState.errorEmitted = true; + process.nextTick(emitErrorAndCloseNT, _this, err); + } else { + process.nextTick(emitCloseNT, _this); + } + } else if (cb) { + process.nextTick(emitCloseNT, _this); + cb(err); + } else { + process.nextTick(emitCloseNT, _this); + } + }); + + return this; +} + +function emitErrorAndCloseNT(self, err) { + emitErrorNT(self, err); + emitCloseNT(self); +} + +function emitCloseNT(self) { + if (self._writableState && !self._writableState.emitClose) return; + if (self._readableState && !self._readableState.emitClose) return; + self.emit('close'); +} + +function undestroy() { + if (this._readableState) { + this._readableState.destroyed = false; + this._readableState.reading = false; + this._readableState.ended = false; + this._readableState.endEmitted = false; + } + + if (this._writableState) { + this._writableState.destroyed = false; + this._writableState.ended = false; + this._writableState.ending = false; + this._writableState.finalCalled = false; + this._writableState.prefinished = false; + this._writableState.finished = false; + this._writableState.errorEmitted = false; + } +} + +function emitErrorNT(self, err) { + self.emit('error', err); +} + +function errorOrDestroy(stream, err) { + // We have tests that rely on errors being emitted + // in the same tick, so changing this is semver major. + // For now when you opt-in to autoDestroy we allow + // the error to be emitted nextTick. In a future + // semver major update we should change the default to this. + var rState = stream._readableState; + var wState = stream._writableState; + if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err); +} + +module.exports = { + destroy: destroy, + undestroy: undestroy, + errorOrDestroy: errorOrDestroy +}; + +/***/ }), + +/***/ 238: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; +// Ported from https://github.com/mafintosh/pump with +// permission from the author, Mathias Buus (@mafintosh). + + +var eos; + +function once(callback) { + var called = false; + return function () { + if (called) return; + called = true; + callback.apply(void 0, arguments); + }; +} + +var _require$codes = __webpack_require__(563).codes, + ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS, + ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED; + +function noop(err) { + // Rethrow the error if it exists to avoid swallowing it + if (err) throw err; +} + +function isRequest(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +} + +function destroyer(stream, reading, writing, callback) { + callback = once(callback); + var closed = false; + stream.on('close', function () { + closed = true; + }); + if (eos === undefined) eos = __webpack_require__(740); + eos(stream, { + readable: reading, + writable: writing + }, function (err) { + if (err) return callback(err); + closed = true; + callback(); + }); + var destroyed = false; + return function (err) { + if (closed) return; + if (destroyed) return; + destroyed = true; // request.destroy just do .end - .abort is what we want + + if (isRequest(stream)) return stream.abort(); + if (typeof stream.destroy === 'function') return stream.destroy(); + callback(err || new ERR_STREAM_DESTROYED('pipe')); + }; +} + +function call(fn) { + fn(); +} + +function pipe(from, to) { + return from.pipe(to); +} + +function popCallback(streams) { + if (!streams.length) return noop; + if (typeof streams[streams.length - 1] !== 'function') return noop; + return streams.pop(); +} + +function pipeline() { + for (var _len = arguments.length, streams = new Array(_len), _key = 0; _key < _len; _key++) { + streams[_key] = arguments[_key]; + } + + var callback = popCallback(streams); + if (Array.isArray(streams[0])) streams = streams[0]; + + if (streams.length < 2) { + throw new ERR_MISSING_ARGS('streams'); + } + + var error; + var destroys = streams.map(function (stream, i) { + var reading = i < streams.length - 1; + var writing = i > 0; + return destroyer(stream, reading, writing, function (err) { + if (!error) error = err; + if (err) destroys.forEach(call); + if (reading) return; + destroys.forEach(call); + callback(error); + }); + }); + return streams.reduce(pipe); +} + +module.exports = pipeline; + +/***/ }), + +/***/ 241: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// A bit simpler than readable streams. +// Implement an async ._write(chunk, encoding, cb), and it'll handle all +// the drain event emission and buffering. + + +module.exports = Writable; +/* */ + +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; + this.next = null; +} // It seems a linked list but it is not +// there will be only 2 of these for each stream + + +function CorkedRequest(state) { + var _this = this; + + this.next = null; + this.entry = null; + + this.finish = function () { + onCorkedFinish(_this, state); + }; +} +/* */ + +/**/ + + +var Duplex; +/**/ + +Writable.WritableState = WritableState; +/**/ + +var internalUtil = { + deprecate: __webpack_require__(917) +}; +/**/ + +/**/ + +var Stream = __webpack_require__(427); +/**/ + + +var Buffer = __webpack_require__(293).Buffer; + +var OurUint8Array = global.Uint8Array || function () {}; + +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} + +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} + +var destroyImpl = __webpack_require__(232); + +var _require = __webpack_require__(216), + getHighWaterMark = _require.getHighWaterMark; + +var _require$codes = __webpack_require__(563).codes, + ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, + ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, + ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, + ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE, + ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED, + ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES, + ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END, + ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING; + +var errorOrDestroy = destroyImpl.errorOrDestroy; + +__webpack_require__(689)(Writable, Stream); + +function nop() {} + +function WritableState(options, stream, isDuplex) { + Duplex = Duplex || __webpack_require__(831); + options = options || {}; // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream, + // e.g. options.readableObjectMode vs. options.writableObjectMode, etc. + + if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag to indicate whether or not this stream + // contains buffers or objects. + + this.objectMode = !!options.objectMode; + if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + + this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); // if _final has been called + + this.finalCalled = false; // drain event flag. + + this.needDrain = false; // at the start of calling end() + + this.ending = false; // when end() has been called, and returned + + this.ended = false; // when 'finish' is emitted + + this.finished = false; // has it been destroyed + + this.destroyed = false; // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + + this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + + this.length = 0; // a flag to see when we're in the middle of a write. + + this.writing = false; // when true all writes will be buffered until .uncork() call + + this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + + this.sync = true; // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + + this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb) + + this.onwrite = function (er) { + onwrite(stream, er); + }; // the callback that the user supplies to write(chunk,encoding,cb) + + + this.writecb = null; // the amount that is being written when _write is called. + + this.writelen = 0; + this.bufferedRequest = null; + this.lastBufferedRequest = null; // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + + this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + + this.prefinished = false; // True if the error was already emitted and should not be thrown again + + this.errorEmitted = false; // Should close be emitted on destroy. Defaults to true. + + this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'finish' (and potentially 'end') + + this.autoDestroy = !!options.autoDestroy; // count buffered requests + + this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always + // one allocated and free to use, and we maintain at most two + + this.corkedRequestsFree = new CorkedRequest(this); +} + +WritableState.prototype.getBuffer = function getBuffer() { + var current = this.bufferedRequest; + var out = []; + + while (current) { + out.push(current); + current = current.next; + } + + return out; +}; + +(function () { + try { + Object.defineProperty(WritableState.prototype, 'buffer', { + get: internalUtil.deprecate(function writableStateBufferGetter() { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') + }); + } catch (_) {} +})(); // Test _writableState for inheritance to account for Duplex streams, +// whose prototype chain only points to Readable. + + +var realHasInstance; + +if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { + realHasInstance = Function.prototype[Symbol.hasInstance]; + Object.defineProperty(Writable, Symbol.hasInstance, { + value: function value(object) { + if (realHasInstance.call(this, object)) return true; + if (this !== Writable) return false; + return object && object._writableState instanceof WritableState; + } + }); +} else { + realHasInstance = function realHasInstance(object) { + return object instanceof this; + }; +} + +function Writable(options) { + Duplex = Duplex || __webpack_require__(831); // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. + // Trying to use the custom `instanceof` for Writable here will also break the + // Node.js LazyTransform implementation, which has a non-trivial getter for + // `_writableState` that would lead to infinite recursion. + // Checking for a Stream.Duplex instance is faster here instead of inside + // the WritableState constructor, at least with V8 6.5 + + var isDuplex = this instanceof Duplex; + if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options); + this._writableState = new WritableState(options, this, isDuplex); // legacy. + + this.writable = true; + + if (options) { + if (typeof options.write === 'function') this._write = options.write; + if (typeof options.writev === 'function') this._writev = options.writev; + if (typeof options.destroy === 'function') this._destroy = options.destroy; + if (typeof options.final === 'function') this._final = options.final; + } + + Stream.call(this); +} // Otherwise people can pipe Writable streams, which is just wrong. + + +Writable.prototype.pipe = function () { + errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); +}; + +function writeAfterEnd(stream, cb) { + var er = new ERR_STREAM_WRITE_AFTER_END(); // TODO: defer error events consistently everywhere, not just the cb + + errorOrDestroy(stream, er); + process.nextTick(cb, er); +} // Checks that a user-supplied chunk is valid, especially for the particular +// mode the stream is in. Currently this means that `null` is never accepted +// and undefined/non-string values are only allowed in object mode. + + +function validChunk(stream, state, chunk, cb) { + var er; + + if (chunk === null) { + er = new ERR_STREAM_NULL_VALUES(); + } else if (typeof chunk !== 'string' && !state.objectMode) { + er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk); + } + + if (er) { + errorOrDestroy(stream, er); + process.nextTick(cb, er); + return false; + } + + return true; +} + +Writable.prototype.write = function (chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + + var isBuf = !state.objectMode && _isUint8Array(chunk); + + if (isBuf && !Buffer.isBuffer(chunk)) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; + if (typeof cb !== 'function') cb = nop; + if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); + } + return ret; +}; + +Writable.prototype.cork = function () { + this._writableState.corked++; +}; + +Writable.prototype.uncork = function () { + var state = this._writableState; + + if (state.corked) { + state.corked--; + if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); + } +}; + +Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { + // node::ParseEncoding() requires lower case. + if (typeof encoding === 'string') encoding = encoding.toLowerCase(); + if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding); + this._writableState.defaultEncoding = encoding; + return this; +}; + +Object.defineProperty(Writable.prototype, 'writableBuffer', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState && this._writableState.getBuffer(); + } +}); + +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { + chunk = Buffer.from(chunk, encoding); + } + + return chunk; +} + +Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState.highWaterMark; + } +}); // if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. + +function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { + if (!isBuf) { + var newChunk = decodeChunk(state, chunk, encoding); + + if (chunk !== newChunk) { + isBuf = true; + encoding = 'buffer'; + chunk = newChunk; + } + } + + var len = state.objectMode ? 1 : chunk.length; + state.length += len; + var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false. + + if (!ret) state.needDrain = true; + + if (state.writing || state.corked) { + var last = state.lastBufferedRequest; + state.lastBufferedRequest = { + chunk: chunk, + encoding: encoding, + isBuf: isBuf, + callback: cb, + next: null + }; + + if (last) { + last.next = state.lastBufferedRequest; + } else { + state.bufferedRequest = state.lastBufferedRequest; + } + + state.bufferedRequestCount += 1; + } else { + doWrite(stream, state, false, len, chunk, encoding, cb); + } + + return ret; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, sync, er, cb) { + --state.pendingcb; + + if (sync) { + // defer the callback if we are being called synchronously + // to avoid piling up things on the stack + process.nextTick(cb, er); // this can emit finish, and it will always happen + // after error + + process.nextTick(finishMaybe, stream, state); + stream._writableState.errorEmitted = true; + errorOrDestroy(stream, er); + } else { + // the caller expect this to happen before if + // it is async + cb(er); + stream._writableState.errorEmitted = true; + errorOrDestroy(stream, er); // this can emit finish, but finish must + // always follow error + + finishMaybe(stream, state); + } +} + +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} + +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; + if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK(); + onwriteStateUpdate(state); + if (er) onwriteError(stream, state, sync, er, cb);else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(state) || stream.destroyed; + + if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { + clearBuffer(stream, state); + } + + if (sync) { + process.nextTick(afterWrite, stream, state, finished, cb); + } else { + afterWrite(stream, state, finished, cb); + } + } +} + +function afterWrite(stream, state, finished, cb) { + if (!finished) onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} // Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. + + +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); + } +} // if there's something in the buffer waiting, then process it + + +function clearBuffer(stream, state) { + state.bufferProcessing = true; + var entry = state.bufferedRequest; + + if (stream._writev && entry && entry.next) { + // Fast case, write everything using _writev() + var l = state.bufferedRequestCount; + var buffer = new Array(l); + var holder = state.corkedRequestsFree; + holder.entry = entry; + var count = 0; + var allBuffers = true; + + while (entry) { + buffer[count] = entry; + if (!entry.isBuf) allBuffers = false; + entry = entry.next; + count += 1; + } + + buffer.allBuffers = allBuffers; + doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time + // as the hot path ends with doWrite + + state.pendingcb++; + state.lastBufferedRequest = null; + + if (holder.next) { + state.corkedRequestsFree = holder.next; + holder.next = null; + } else { + state.corkedRequestsFree = new CorkedRequest(state); + } + + state.bufferedRequestCount = 0; + } else { + // Slow case, write chunks one-by-one + while (entry) { + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; + doWrite(stream, state, false, len, chunk, encoding, cb); + entry = entry.next; + state.bufferedRequestCount--; // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + + if (state.writing) { + break; + } + } + + if (entry === null) state.lastBufferedRequest = null; + } + + state.bufferedRequest = entry; + state.bufferProcessing = false; +} + +Writable.prototype._write = function (chunk, encoding, cb) { + cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()')); +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function (chunk, encoding, cb) { + var state = this._writableState; + + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks + + if (state.corked) { + state.corked = 1; + this.uncork(); + } // ignore unnecessary end() calls. + + + if (!state.ending) endWritable(this, state, cb); + return this; +}; + +Object.defineProperty(Writable.prototype, 'writableLength', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState.length; + } +}); + +function needFinish(state) { + return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; +} + +function callFinal(stream, state) { + stream._final(function (err) { + state.pendingcb--; + + if (err) { + errorOrDestroy(stream, err); + } + + state.prefinished = true; + stream.emit('prefinish'); + finishMaybe(stream, state); + }); +} + +function prefinish(stream, state) { + if (!state.prefinished && !state.finalCalled) { + if (typeof stream._final === 'function' && !state.destroyed) { + state.pendingcb++; + state.finalCalled = true; + process.nextTick(callFinal, stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); + } + } +} + +function finishMaybe(stream, state) { + var need = needFinish(state); + + if (need) { + prefinish(stream, state); + + if (state.pendingcb === 0) { + state.finished = true; + stream.emit('finish'); + + if (state.autoDestroy) { + // In case of duplex streams we need a way to detect + // if the readable side is ready for autoDestroy as well + var rState = stream._readableState; + + if (!rState || rState.autoDestroy && rState.endEmitted) { + stream.destroy(); + } + } + } + } + + return need; +} + +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + + if (cb) { + if (state.finished) process.nextTick(cb);else stream.once('finish', cb); + } + + state.ended = true; + stream.writable = false; +} + +function onCorkedFinish(corkReq, state, err) { + var entry = corkReq.entry; + corkReq.entry = null; + + while (entry) { + var cb = entry.callback; + state.pendingcb--; + cb(err); + entry = entry.next; + } // reuse the free corkReq. + + + state.corkedRequestsFree.next = corkReq; +} + +Object.defineProperty(Writable.prototype, 'destroyed', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + if (this._writableState === undefined) { + return false; + } + + return this._writableState.destroyed; + }, + set: function set(value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._writableState) { + return; + } // backward compatibility, the user is explicitly + // managing destroyed + + + this._writableState.destroyed = value; + } +}); +Writable.prototype.destroy = destroyImpl.destroy; +Writable.prototype._undestroy = destroyImpl.undestroy; + +Writable.prototype._destroy = function (err, cb) { + cb(err); +}; + +/***/ }), + +/***/ 244: +/***/ (function(module) { + +function spliceOne(list, index) { + for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) + list[i] = list[k]; + list.pop(); +} + +function Manager(interval, streamInterval, kaCountMax) { + var streams = this._streams = []; + this._timer = undefined; + this._timerInterval = interval; + this._timerfn = function() { + var now = Date.now(); + for (var i = 0, len = streams.length, s, last; i < len; ++i) { + s = streams[i]; + last = s._kalast; + if (last && (now - last) >= streamInterval) { + if (++s._kacnt > kaCountMax) { + var err = new Error('Keepalive timeout'); + err.level = 'client-timeout'; + s.emit('error', err); + s.disconnect(); + spliceOne(streams, i); + --i; + len = streams.length; + } else { + s._kalast = now; + // XXX: if the server ever starts sending real global requests to the + // client, we will need to add a dummy callback here to keep the + // correct reply order + s.ping(); + } + } + } + }; +} + +Manager.prototype.start = function() { + if (this._timer) + this.stop(); + this._timer = setInterval(this._timerfn, this._timerInterval); +}; + +Manager.prototype.stop = function() { + if (this._timer) { + clearInterval(this._timer); + this._timer = undefined; + } +}; + +Manager.prototype.add = function(stream) { + var streams = this._streams, + self = this; + + stream.once('end', function() { + self.remove(stream); + }).on('packet', resetKA); + + streams[streams.length] = stream; + + resetKA(); + + if (!this._timer) + this.start(); + + function resetKA() { + stream._kalast = Date.now(); + stream._kacnt = 0; + } +}; + +Manager.prototype.remove = function(stream) { + var streams = this._streams, + index = streams.indexOf(stream); + if (index > -1) + spliceOne(streams, index); + if (!streams.length) + this.stop(); +}; + +module.exports = Manager; + + +/***/ }), + +/***/ 249: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2011 Mark Cavage All rights reserved. + +var errors = __webpack_require__(584); +var types = __webpack_require__(362); + +var Reader = __webpack_require__(733); +var Writer = __webpack_require__(998); + + +// --- Exports + +module.exports = { + + Reader: Reader, + + Writer: Writer + +}; + +for (var t in types) { + if (types.hasOwnProperty(t)) + module.exports[t] = types[t]; +} +for (var e in errors) { + if (errors.hasOwnProperty(e)) + module.exports[e] = errors[e]; +} + + +/***/ }), + +/***/ 255: +/***/ (function(module) { + +// https://github.com/HenrikJoreteg/extend-object/blob/v0.1.0/extend-object.js + +var arr = []; +var each = arr.forEach; +var slice = arr.slice; + +module.exports.extend = function(obj) { + each.call(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; +}; + +module.exports.processArgs = function(opts, callback, defaultOpts) { + if (!callback && typeof opts === 'function') { + callback = opts; + opts = null; + } + return { + callback: callback, + opts: module.exports.extend({}, defaultOpts, opts) + }; +}; + + +/** + * Parse the given repo tag name (as a string) and break it out into repo/tag pair. + * // if given the input http://localhost:8080/woot:latest + * { + * repository: 'http://localhost:8080/woot', + * tag: 'latest' + * } + * @param {String} input Input e.g: 'repo/foo', 'ubuntu', 'ubuntu:latest' + * @return {Object} input parsed into the repo and tag. + */ +module.exports.parseRepositoryTag = function(input) { + var separatorPos; + var digestPos = input.indexOf('@'); + var colonPos = input.lastIndexOf(':'); + // @ symbol is more important + if (digestPos >= 0) { + separatorPos = digestPos; + } else if (colonPos >= 0) { + separatorPos = colonPos; + } else { + // no colon nor @ + return { + repository: input + }; + } + + // last colon is either the tag (or part of a port designation) + var tag = input.slice(separatorPos + 1); + + // if it contains a / its not a tag and is part of the url + if (tag.indexOf('/') === -1) { + return { + repository: input.slice(0, separatorPos), + tag: tag + }; + } + + return { + repository: input + }; +}; + + +/***/ }), + +/***/ 279: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var net = __webpack_require__(631); +var EventEmitter = __webpack_require__(614).EventEmitter; +var listenerCount = EventEmitter.listenerCount; +var inherits = __webpack_require__(669).inherits; + +var ssh2_streams = __webpack_require__(139); +var parseKey = ssh2_streams.utils.parseKey; +var SSH2Stream = ssh2_streams.SSH2Stream; +var SFTPStream = ssh2_streams.SFTPStream; +var consts = ssh2_streams.constants; +var DISCONNECT_REASON = consts.DISCONNECT_REASON; +var CHANNEL_OPEN_FAILURE = consts.CHANNEL_OPEN_FAILURE; +var ALGORITHMS = consts.ALGORITHMS; + +var Channel = __webpack_require__(89); +var KeepaliveManager = __webpack_require__(244); +var writeUInt32BE = __webpack_require__(58).writeUInt32BE; + +var MAX_CHANNEL = Math.pow(2, 32) - 1; +var MAX_PENDING_AUTHS = 10; + +var kaMgr; + +function Server(cfg, listener) { + if (!(this instanceof Server)) + return new Server(cfg, listener); + + var hostKeys = { + 'ssh-rsa': null, + 'ssh-dss': null, + 'ssh-ed25519': null, + 'ecdsa-sha2-nistp256': null, + 'ecdsa-sha2-nistp384': null, + 'ecdsa-sha2-nistp521': null + }; + + var hostKeys_ = cfg.hostKeys; + if (!Array.isArray(hostKeys_)) + throw new Error('hostKeys must be an array'); + + var i; + for (i = 0; i < hostKeys_.length; ++i) { + var privateKey; + if (Buffer.isBuffer(hostKeys_[i]) || typeof hostKeys_[i] === 'string') + privateKey = parseKey(hostKeys_[i]); + else + privateKey = parseKey(hostKeys_[i].key, hostKeys_[i].passphrase); + if (privateKey instanceof Error) + throw new Error('Cannot parse privateKey: ' + privateKey.message); + if (Array.isArray(privateKey)) + privateKey = privateKey[0]; // OpenSSH's newer format only stores 1 key for now + if (privateKey.getPrivatePEM() === null) + throw new Error('privateKey value contains an invalid private key'); + if (hostKeys[privateKey.type]) + continue; + hostKeys[privateKey.type] = privateKey; + } + + var algorithms = { + kex: undefined, + kexBuf: undefined, + cipher: undefined, + cipherBuf: undefined, + serverHostKey: undefined, + serverHostKeyBuf: undefined, + hmac: undefined, + hmacBuf: undefined, + compress: undefined, + compressBuf: undefined + }; + if (typeof cfg.algorithms === 'object' && cfg.algorithms !== null) { + var algosSupported; + var algoList; + + algoList = cfg.algorithms.kex; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_KEX; + for (i = 0; i < algoList.length; ++i) { + if (algosSupported.indexOf(algoList[i]) === -1) + throw new Error('Unsupported key exchange algorithm: ' + algoList[i]); + } + algorithms.kex = algoList; + } + + algoList = cfg.algorithms.cipher; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_CIPHER; + for (i = 0; i < algoList.length; ++i) { + if (algosSupported.indexOf(algoList[i]) === -1) + throw new Error('Unsupported cipher algorithm: ' + algoList[i]); + } + algorithms.cipher = algoList; + } + + algoList = cfg.algorithms.serverHostKey; + var copied = false; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_SERVER_HOST_KEY; + for (i = algoList.length - 1; i >= 0; --i) { + if (algosSupported.indexOf(algoList[i]) === -1) { + throw new Error('Unsupported server host key algorithm: ' + + algoList[i]); + } + if (!hostKeys[algoList[i]]) { + // Silently discard for now + if (!copied) { + algoList = algoList.slice(); + copied = true; + } + algoList.splice(i, 1); + } + } + if (algoList.length > 0) + algorithms.serverHostKey = algoList; + } + + algoList = cfg.algorithms.hmac; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_HMAC; + for (i = 0; i < algoList.length; ++i) { + if (algosSupported.indexOf(algoList[i]) === -1) + throw new Error('Unsupported HMAC algorithm: ' + algoList[i]); + } + algorithms.hmac = algoList; + } + + algoList = cfg.algorithms.compress; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_COMPRESS; + for (i = 0; i < algoList.length; ++i) { + if (algosSupported.indexOf(algoList[i]) === -1) + throw new Error('Unsupported compression algorithm: ' + algoList[i]); + } + algorithms.compress = algoList; + } + } + + // Make sure we at least have some kind of valid list of support key + // formats + if (algorithms.serverHostKey === undefined) { + var hostKeyAlgos = Object.keys(hostKeys); + for (i = hostKeyAlgos.length - 1; i >= 0; --i) { + if (!hostKeys[hostKeyAlgos[i]]) + hostKeyAlgos.splice(i, 1); + } + algorithms.serverHostKey = hostKeyAlgos; + } + + if (!kaMgr + && Server.KEEPALIVE_INTERVAL > 0 + && Server.KEEPALIVE_CLIENT_INTERVAL > 0 + && Server.KEEPALIVE_CLIENT_COUNT_MAX >= 0) { + kaMgr = new KeepaliveManager(Server.KEEPALIVE_INTERVAL, + Server.KEEPALIVE_CLIENT_INTERVAL, + Server.KEEPALIVE_CLIENT_COUNT_MAX); + } + + var self = this; + + EventEmitter.call(this); + + if (typeof listener === 'function') + self.on('connection', listener); + + var streamcfg = { + algorithms: algorithms, + hostKeys: hostKeys, + server: true + }; + var keys; + var len; + for (i = 0, keys = Object.keys(cfg), len = keys.length; i < len; ++i) { + var key = keys[i]; + if (key === 'privateKey' + || key === 'publicKey' + || key === 'passphrase' + || key === 'algorithms' + || key === 'hostKeys' + || key === 'server') { + continue; + } + streamcfg[key] = cfg[key]; + } + + if (typeof streamcfg.debug === 'function') { + var oldDebug = streamcfg.debug; + var cfgKeys = Object.keys(streamcfg); + } + + this._srv = new net.Server(function(socket) { + if (self._connections >= self.maxConnections) { + socket.destroy(); + return; + } + ++self._connections; + socket.once('close', function(had_err) { + --self._connections; + + // since joyent/node#993bb93e0a, we have to "read past EOF" in order to + // get an `end` event on streams. thankfully adding this does not + // negatively affect node versions pre-joyent/node#993bb93e0a. + sshstream.read(); + }).on('error', function(err) { + sshstream.reset(); + sshstream.emit('error', err); + }); + + var conncfg = streamcfg; + + // prepend debug output with a unique identifier in case there are multiple + // clients connected at the same time + if (oldDebug) { + conncfg = {}; + for (var i = 0, key; i < cfgKeys.length; ++i) { + key = cfgKeys[i]; + conncfg[key] = streamcfg[key]; + } + var debugPrefix = '[' + process.hrtime().join('.') + '] '; + conncfg.debug = function(msg) { + oldDebug(debugPrefix + msg); + }; + } + + var sshstream = new SSH2Stream(conncfg); + var client = new Client(sshstream, socket); + + socket.pipe(sshstream).pipe(socket); + + // silence pre-header errors + function onClientPreHeaderError(err) {} + client.on('error', onClientPreHeaderError); + + sshstream.once('header', function(header) { + if (sshstream._readableState.ended) { + // already disconnected internally in SSH2Stream due to incompatible + // protocol version + return; + } else if (!listenerCount(self, 'connection')) { + // auto reject + return sshstream.disconnect(DISCONNECT_REASON.BY_APPLICATION); + } + + client.removeListener('error', onClientPreHeaderError); + + self.emit('connection', + client, + { ip: socket.remoteAddress, + family: socket.remoteFamily, + port: socket.remotePort, + header: header }); + }); + }).on('error', function(err) { + self.emit('error', err); + }).on('listening', function() { + self.emit('listening'); + }).on('close', function() { + self.emit('close'); + }); + this._connections = 0; + this.maxConnections = Infinity; +} +inherits(Server, EventEmitter); + +Server.prototype.listen = function() { + this._srv.listen.apply(this._srv, arguments); + return this; +}; + +Server.prototype.address = function() { + return this._srv.address(); +}; + +Server.prototype.getConnections = function(cb) { + this._srv.getConnections(cb); +}; + +Server.prototype.close = function(cb) { + this._srv.close(cb); + return this; +}; + +Server.prototype.ref = function() { + this._srv.ref(); +}; + +Server.prototype.unref = function() { + this._srv.unref(); +}; + + +function Client(stream, socket) { + EventEmitter.call(this); + + var self = this; + + this._sshstream = stream; + var channels = this._channels = {}; + this._curChan = -1; + this._sock = socket; + this.noMoreSessions = false; + this.authenticated = false; + + stream.on('end', function() { + socket.resume(); + self.emit('end'); + }).on('close', function(hasErr) { + self.emit('close', hasErr); + }).on('error', function(err) { + self.emit('error', err); + }).on('drain', function() { + self.emit('drain'); + }).on('continue', function() { + self.emit('continue'); + }); + + var exchanges = 0; + var acceptedAuthSvc = false; + var pendingAuths = []; + var authCtx; + + // begin service/auth-related ================================================ + stream.on('SERVICE_REQUEST', function(service) { + if (exchanges === 0 + || acceptedAuthSvc + || self.authenticated + || service !== 'ssh-userauth') + return stream.disconnect(DISCONNECT_REASON.SERVICE_NOT_AVAILABLE); + + acceptedAuthSvc = true; + stream.serviceAccept(service); + }).on('USERAUTH_REQUEST', onUSERAUTH_REQUEST); + function onUSERAUTH_REQUEST(username, service, method, methodData) { + if (exchanges === 0 + || (authCtx + && (authCtx.username !== username || authCtx.service !== service)) + // TODO: support hostbased auth + || (method !== 'password' + && method !== 'publickey' + && method !== 'hostbased' + && method !== 'keyboard-interactive' + && method !== 'none') + || pendingAuths.length === MAX_PENDING_AUTHS) + return stream.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR); + else if (service !== 'ssh-connection') + return stream.disconnect(DISCONNECT_REASON.SERVICE_NOT_AVAILABLE); + + // XXX: this really shouldn't be reaching into private state ... + stream._state.authMethod = method; + + var ctx; + if (method === 'keyboard-interactive') { + ctx = new KeyboardAuthContext(stream, username, service, method, + methodData, onAuthDecide); + } else if (method === 'publickey') { + ctx = new PKAuthContext(stream, username, service, method, methodData, + onAuthDecide); + } else if (method === 'hostbased') { + ctx = new HostbasedAuthContext(stream, username, service, method, + methodData, onAuthDecide); + } else if (method === 'password') { + ctx = new PwdAuthContext(stream, username, service, method, methodData, + onAuthDecide); + } else if (method === 'none') + ctx = new AuthContext(stream, username, service, method, onAuthDecide); + + if (authCtx) { + if (!authCtx._initialResponse) + return pendingAuths.push(ctx); + else if (authCtx._multistep && !this._finalResponse) { + // RFC 4252 says to silently abort the current auth request if a new + // auth request comes in before the final response from an auth method + // that requires additional request/response exchanges -- this means + // keyboard-interactive for now ... + authCtx._cleanup && authCtx._cleanup(); + authCtx.emit('abort'); + } + } + + authCtx = ctx; + + if (listenerCount(self, 'authentication')) + self.emit('authentication', authCtx); + else + authCtx.reject(); + } + function onAuthDecide(ctx, allowed, methodsLeft, isPartial) { + if (authCtx === ctx && !self.authenticated) { + if (allowed) { + stream.removeListener('USERAUTH_REQUEST', onUSERAUTH_REQUEST); + authCtx = undefined; + self.authenticated = true; + stream.authSuccess(); + pendingAuths = []; + self.emit('ready'); + } else { + stream.authFailure(methodsLeft, isPartial); + if (pendingAuths.length) { + authCtx = pendingAuths.pop(); + if (listenerCount(self, 'authentication')) + self.emit('authentication', authCtx); + else + authCtx.reject(); + } + } + } + } + // end service/auth-related ================================================== + + var unsentGlobalRequestsReplies = []; + + function sendReplies() { + var reply; + while (unsentGlobalRequestsReplies.length > 0 + && unsentGlobalRequestsReplies[0].type) { + reply = unsentGlobalRequestsReplies.shift(); + if (reply.type === 'SUCCESS') + stream.requestSuccess(reply.buf); + if (reply.type === 'FAILURE') + stream.requestFailure(); + } + } + + stream.on('GLOBAL_REQUEST', function(name, wantReply, data) { + var reply = { + type: null, + buf: null + }; + + function setReply(type, buf) { + reply.type = type; + reply.buf = buf; + sendReplies(); + } + + if (wantReply) + unsentGlobalRequestsReplies.push(reply); + + if ((name === 'tcpip-forward' + || name === 'cancel-tcpip-forward' + || name === 'no-more-sessions@openssh.com' + || name === 'streamlocal-forward@openssh.com' + || name === 'cancel-streamlocal-forward@openssh.com') + && listenerCount(self, 'request') + && self.authenticated) { + var accept; + var reject; + + if (wantReply) { + var replied = false; + accept = function(chosenPort) { + if (replied) + return; + replied = true; + var bufPort; + if (name === 'tcpip-forward' + && data.bindPort === 0 + && typeof chosenPort === 'number') { + bufPort = Buffer.allocUnsafe(4); + writeUInt32BE(bufPort, chosenPort, 0); + } + setReply('SUCCESS', bufPort); + }; + reject = function() { + if (replied) + return; + replied = true; + setReply('FAILURE'); + }; + } + + if (name === 'no-more-sessions@openssh.com') { + self.noMoreSessions = true; + accept && accept(); + return; + } + + self.emit('request', accept, reject, name, data); + } else if (wantReply) + setReply('FAILURE'); + }); + + stream.on('CHANNEL_OPEN', function(info) { + // do early reject in some cases to prevent wasteful channel allocation + if ((info.type === 'session' && self.noMoreSessions) + || !self.authenticated) { + var reasonCode = CHANNEL_OPEN_FAILURE.ADMINISTRATIVELY_PROHIBITED; + return stream.channelOpenFail(info.sender, reasonCode); + } + + var localChan = nextChannel(self); + var accept; + var reject; + var replied = false; + if (localChan === false) { + // auto-reject due to no channels available + return stream.channelOpenFail(info.sender, + CHANNEL_OPEN_FAILURE.RESOURCE_SHORTAGE); + } + + // be optimistic, reserve channel to prevent another request from trying to + // take the same channel + channels[localChan] = true; + + reject = function() { + if (replied) + return; + + replied = true; + + delete channels[localChan]; + + var reasonCode = CHANNEL_OPEN_FAILURE.ADMINISTRATIVELY_PROHIBITED; + return stream.channelOpenFail(info.sender, reasonCode); + }; + + switch (info.type) { + case 'session': + if (listenerCount(self, 'session')) { + accept = function() { + if (replied) + return; + + replied = true; + + stream.channelOpenConfirm(info.sender, + localChan, + Channel.MAX_WINDOW, + Channel.PACKET_SIZE); + + return new Session(self, info, localChan); + }; + + self.emit('session', accept, reject); + } else + reject(); + break; + case 'direct-tcpip': + if (listenerCount(self, 'tcpip')) { + accept = function() { + if (replied) + return; + + replied = true; + + stream.channelOpenConfirm(info.sender, + localChan, + Channel.MAX_WINDOW, + Channel.PACKET_SIZE); + + var chaninfo = { + type: undefined, + incoming: { + id: localChan, + window: Channel.MAX_WINDOW, + packetSize: Channel.PACKET_SIZE, + state: 'open' + }, + outgoing: { + id: info.sender, + window: info.window, + packetSize: info.packetSize, + state: 'open' + } + }; + + return new Channel(chaninfo, self); + }; + + self.emit('tcpip', accept, reject, info.data); + } else + reject(); + break; + case 'direct-streamlocal@openssh.com': + if (listenerCount(self, 'openssh.streamlocal')) { + accept = function() { + if (replied) + return; + + replied = true; + + stream.channelOpenConfirm(info.sender, + localChan, + Channel.MAX_WINDOW, + Channel.PACKET_SIZE); + + var chaninfo = { + type: undefined, + incoming: { + id: localChan, + window: Channel.MAX_WINDOW, + packetSize: Channel.PACKET_SIZE, + state: 'open' + }, + outgoing: { + id: info.sender, + window: info.window, + packetSize: info.packetSize, + state: 'open' + } + }; + + return new Channel(chaninfo, self); + }; + + self.emit('openssh.streamlocal', accept, reject, info.data); + } else + reject(); + break; + default: + // auto-reject unsupported channel types + reject(); + } + }); + + stream.on('NEWKEYS', function() { + if (++exchanges > 1) + self.emit('rekey'); + }); + + if (kaMgr) { + this.once('ready', function() { + kaMgr.add(stream); + }); + } +} +inherits(Client, EventEmitter); + +Client.prototype.end = function() { + return this._sshstream.disconnect(DISCONNECT_REASON.BY_APPLICATION); +}; + +Client.prototype.x11 = function(originAddr, originPort, cb) { + var opts = { + originAddr: originAddr, + originPort: originPort + }; + return openChannel(this, 'x11', opts, cb); +}; + +Client.prototype.forwardOut = function(boundAddr, boundPort, remoteAddr, + remotePort, cb) { + var opts = { + boundAddr: boundAddr, + boundPort: boundPort, + remoteAddr: remoteAddr, + remotePort: remotePort + }; + return openChannel(this, 'forwarded-tcpip', opts, cb); +}; + +Client.prototype.openssh_forwardOutStreamLocal = function(socketPath, cb) { + var opts = { + socketPath: socketPath + }; + return openChannel(this, 'forwarded-streamlocal@openssh.com', opts, cb); +}; + +Client.prototype.rekey = function(cb) { + var stream = this._sshstream; + var ret = true; + var error; + + try { + ret = stream.rekey(); + } catch (ex) { + error = ex; + } + + // TODO: re-throw error if no callback? + + if (typeof cb === 'function') { + if (error) { + process.nextTick(function() { + cb(error); + }); + } else + this.once('rekey', cb); + } + + return ret; +}; + +function Session(client, info, localChan) { + this.subtype = undefined; + + var ending = false; + var self = this; + var outgoingId = info.sender; + var channel; + + var chaninfo = { + type: 'session', + incoming: { + id: localChan, + window: Channel.MAX_WINDOW, + packetSize: Channel.PACKET_SIZE, + state: 'open' + }, + outgoing: { + id: info.sender, + window: info.window, + packetSize: info.packetSize, + state: 'open' + } + }; + + function onREQUEST(info) { + var replied = false; + var accept; + var reject; + + if (info.wantReply) { + // "real session" requests will have custom accept behaviors + if (info.request !== 'shell' + && info.request !== 'exec' + && info.request !== 'subsystem') { + accept = function() { + if (replied || ending || channel) + return; + + replied = true; + + return client._sshstream.channelSuccess(outgoingId); + }; + } + + reject = function() { + if (replied || ending || channel) + return; + + replied = true; + + return client._sshstream.channelFailure(outgoingId); + }; + } + + if (ending) { + reject && reject(); + return; + } + + switch (info.request) { + // "pre-real session start" requests + case 'env': + if (listenerCount(self, 'env')) { + self.emit('env', accept, reject, { + key: info.key, + val: info.val + }); + } else + reject && reject(); + break; + case 'pty-req': + if (listenerCount(self, 'pty')) { + self.emit('pty', accept, reject, { + cols: info.cols, + rows: info.rows, + width: info.width, + height: info.height, + term: info.term, + modes: info.modes, + }); + } else + reject && reject(); + break; + case 'window-change': + if (listenerCount(self, 'window-change')) { + self.emit('window-change', accept, reject, { + cols: info.cols, + rows: info.rows, + width: info.width, + height: info.height + }); + } else + reject && reject(); + break; + case 'x11-req': + if (listenerCount(self, 'x11')) { + self.emit('x11', accept, reject, { + single: info.single, + protocol: info.protocol, + cookie: info.cookie, + screen: info.screen + }); + } else + reject && reject(); + break; + // "post-real session start" requests + case 'signal': + if (listenerCount(self, 'signal')) { + self.emit('signal', accept, reject, { + name: info.signal + }); + } else + reject && reject(); + break; + // XXX: is `auth-agent-req@openssh.com` really "post-real session start"? + case 'auth-agent-req@openssh.com': + if (listenerCount(self, 'auth-agent')) + self.emit('auth-agent', accept, reject); + else + reject && reject(); + break; + // "real session start" requests + case 'shell': + if (listenerCount(self, 'shell')) { + accept = function() { + if (replied || ending || channel) + return; + + replied = true; + + if (info.wantReply) + client._sshstream.channelSuccess(outgoingId); + + channel = new Channel(chaninfo, client, { server: true }); + + channel.subtype = self.subtype = info.request; + + return channel; + }; + + self.emit('shell', accept, reject); + } else + reject && reject(); + break; + case 'exec': + if (listenerCount(self, 'exec')) { + accept = function() { + if (replied || ending || channel) + return; + + replied = true; + + if (info.wantReply) + client._sshstream.channelSuccess(outgoingId); + + channel = new Channel(chaninfo, client, { server: true }); + + channel.subtype = self.subtype = info.request; + + return channel; + }; + + self.emit('exec', accept, reject, { + command: info.command + }); + } else + reject && reject(); + break; + case 'subsystem': + accept = function() { + if (replied || ending || channel) + return; + + replied = true; + + if (info.wantReply) + client._sshstream.channelSuccess(outgoingId); + + channel = new Channel(chaninfo, client, { server: true }); + + channel.subtype = self.subtype = (info.request + ':' + info.subsystem); + + if (info.subsystem === 'sftp') { + var sftp = new SFTPStream({ + server: true, + debug: client._sshstream.debug + }); + channel.pipe(sftp).pipe(channel); + + return sftp; + } else + return channel; + }; + + if (info.subsystem === 'sftp' && listenerCount(self, 'sftp')) + self.emit('sftp', accept, reject); + else if (info.subsystem !== 'sftp' && listenerCount(self, 'subsystem')) { + self.emit('subsystem', accept, reject, { + name: info.subsystem + }); + } else + reject && reject(); + break; + default: + reject && reject(); + } + } + function onEOF() { + ending = true; + self.emit('eof'); + self.emit('end'); + } + function onCLOSE() { + ending = true; + self.emit('close'); + } + client._sshstream + .on('CHANNEL_REQUEST:' + localChan, onREQUEST) + .once('CHANNEL_EOF:' + localChan, onEOF) + .once('CHANNEL_CLOSE:' + localChan, onCLOSE); +} +inherits(Session, EventEmitter); + + +function AuthContext(stream, username, service, method, cb) { + EventEmitter.call(this); + + var self = this; + + this.username = this.user = username; + this.service = service; + this.method = method; + this._initialResponse = false; + this._finalResponse = false; + this._multistep = false; + this._cbfinal = function(allowed, methodsLeft, isPartial) { + if (!self._finalResponse) { + self._finalResponse = true; + cb(self, allowed, methodsLeft, isPartial); + } + }; + this._stream = stream; +} +inherits(AuthContext, EventEmitter); +AuthContext.prototype.accept = function() { + this._cleanup && this._cleanup(); + this._initialResponse = true; + this._cbfinal(true); +}; +AuthContext.prototype.reject = function(methodsLeft, isPartial) { + this._cleanup && this._cleanup(); + this._initialResponse = true; + this._cbfinal(false, methodsLeft, isPartial); +}; + +var RE_KBINT_SUBMETHODS = /[ \t\r\n]*,[ \t\r\n]*/g; +function KeyboardAuthContext(stream, username, service, method, submethods, cb) { + AuthContext.call(this, stream, username, service, method, cb); + this._multistep = true; + + var self = this; + + this._cb = undefined; + this._onInfoResponse = function(responses) { + if (self._cb) { + var callback = self._cb; + self._cb = undefined; + callback(responses); + } + }; + this.submethods = submethods.split(RE_KBINT_SUBMETHODS); + this.on('abort', function() { + self._cb && self._cb(new Error('Authentication request aborted')); + }); +} +inherits(KeyboardAuthContext, AuthContext); +KeyboardAuthContext.prototype._cleanup = function() { + this._stream.removeListener('USERAUTH_INFO_RESPONSE', this._onInfoResponse); +}; +KeyboardAuthContext.prototype.prompt = function(prompts, title, instructions, + cb) { + if (!Array.isArray(prompts)) + prompts = [ prompts ]; + + if (typeof title === 'function') { + cb = title; + title = instructions = undefined; + } else if (typeof instructions === 'function') { + cb = instructions; + instructions = undefined; + } + + for (var i = 0; i < prompts.length; ++i) { + if (typeof prompts[i] === 'string') { + prompts[i] = { + prompt: prompts[i], + echo: true + }; + } + } + + this._cb = cb; + this._initialResponse = true; + this._stream.once('USERAUTH_INFO_RESPONSE', this._onInfoResponse); + + return this._stream.authInfoReq(title, instructions, prompts); +}; + +function PKAuthContext(stream, username, service, method, pkInfo, cb) { + AuthContext.call(this, stream, username, service, method, cb); + + this.key = { algo: pkInfo.keyAlgo, data: pkInfo.key }; + this.signature = pkInfo.signature; + var sigAlgo; + if (this.signature) { + // TODO: move key type checking logic to ssh2-streams + switch (pkInfo.keyAlgo) { + case 'ssh-rsa': + case 'ssh-dss': + sigAlgo = 'sha1'; + break; + case 'ssh-ed25519': + sigAlgo = null; + break; + case 'ecdsa-sha2-nistp256': + sigAlgo = 'sha256'; + break; + case 'ecdsa-sha2-nistp384': + sigAlgo = 'sha384'; + break; + case 'ecdsa-sha2-nistp521': + sigAlgo = 'sha512'; + break; + } + } + this.sigAlgo = sigAlgo; + this.blob = pkInfo.blob; +} +inherits(PKAuthContext, AuthContext); +PKAuthContext.prototype.accept = function() { + if (!this.signature) { + this._initialResponse = true; + this._stream.authPKOK(this.key.algo, this.key.data); + } else { + AuthContext.prototype.accept.call(this); + } +}; + +function HostbasedAuthContext(stream, username, service, method, pkInfo, cb) { + AuthContext.call(this, stream, username, service, method, cb); + + this.key = { algo: pkInfo.keyAlgo, data: pkInfo.key }; + this.signature = pkInfo.signature; + var sigAlgo; + if (this.signature) { + // TODO: move key type checking logic to ssh2-streams + switch (pkInfo.keyAlgo) { + case 'ssh-rsa': + case 'ssh-dss': + sigAlgo = 'sha1'; + break; + case 'ssh-ed25519': + sigAlgo = null; + break; + case 'ecdsa-sha2-nistp256': + sigAlgo = 'sha256'; + break; + case 'ecdsa-sha2-nistp384': + sigAlgo = 'sha384'; + break; + case 'ecdsa-sha2-nistp521': + sigAlgo = 'sha512'; + break; + } + } + this.sigAlgo = sigAlgo; + this.blob = pkInfo.blob; + this.localHostname = pkInfo.localHostname; + this.localUsername = pkInfo.localUsername; +} +inherits(HostbasedAuthContext, AuthContext); + +function PwdAuthContext(stream, username, service, method, password, cb) { + AuthContext.call(this, stream, username, service, method, cb); + + this.password = password; +} +inherits(PwdAuthContext, AuthContext); + + +function openChannel(self, type, opts, cb) { + // ask the client to open a channel for some purpose + // (e.g. a forwarded TCP connection) + var localChan = nextChannel(self); + var initWindow = Channel.MAX_WINDOW; + var maxPacket = Channel.PACKET_SIZE; + var ret = true; + + if (localChan === false) + return cb(new Error('No free channels available')); + + if (typeof opts === 'function') { + cb = opts; + opts = {}; + } + + self._channels[localChan] = true; + + var sshstream = self._sshstream; + sshstream.once('CHANNEL_OPEN_CONFIRMATION:' + localChan, function(info) { + sshstream.removeAllListeners('CHANNEL_OPEN_FAILURE:' + localChan); + + var chaninfo = { + type: type, + incoming: { + id: localChan, + window: initWindow, + packetSize: maxPacket, + state: 'open' + }, + outgoing: { + id: info.sender, + window: info.window, + packetSize: info.packetSize, + state: 'open' + } + }; + cb(undefined, new Channel(chaninfo, self, { server: true })); + }).once('CHANNEL_OPEN_FAILURE:' + localChan, function(info) { + sshstream.removeAllListeners('CHANNEL_OPEN_CONFIRMATION:' + localChan); + + delete self._channels[localChan]; + + var err = new Error('(SSH) Channel open failure: ' + info.description); + err.reason = info.reason; + err.lang = info.lang; + cb(err); + }); + + if (type === 'forwarded-tcpip') + ret = sshstream.forwardedTcpip(localChan, initWindow, maxPacket, opts); + else if (type === 'x11') + ret = sshstream.x11(localChan, initWindow, maxPacket, opts); + else if (type === 'forwarded-streamlocal@openssh.com') { + ret = sshstream.openssh_forwardedStreamLocal(localChan, + initWindow, + maxPacket, + opts); + } + + return ret; +} + +function nextChannel(self) { + // get the next available channel number + + // fast path + if (self._curChan < MAX_CHANNEL) + return ++self._curChan; + + // slower lookup path + for (var i = 0, channels = self._channels; i < MAX_CHANNEL; ++i) + if (!channels[i]) + return i; + + return false; +} + + +Server.createServer = function(cfg, listener) { + return new Server(cfg, listener); +}; +Server.KEEPALIVE_INTERVAL = 1000; +Server.KEEPALIVE_CLIENT_INTERVAL = 15000; +Server.KEEPALIVE_CLIENT_COUNT_MAX = 3; + +module.exports = Server; +module.exports.IncomingClient = Client; + + +/***/ }), + +/***/ 293: +/***/ (function(module) { + +module.exports = require("buffer"); + +/***/ }), + +/***/ 304: +/***/ (function(module) { + +module.exports = require("string_decoder"); + +/***/ }), + +/***/ 307: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +var HttpAgent = __webpack_require__(605).Agent; +var HttpsAgent = __webpack_require__(211).Agent; +var inherits = __webpack_require__(669).inherits; + +var Client; + +[HttpAgent, HttpsAgent].forEach((ctor) => { + function SSHAgent(connectCfg, agentOptions) { + if (!(this instanceof SSHAgent)) + return new SSHAgent(connectCfg, agentOptions); + + ctor.call(this, agentOptions); + + this._connectCfg = connectCfg; + this._defaultSrcIP = (agentOptions && agentOptions.srcIP) || 'localhost'; + } + inherits(SSHAgent, ctor); + + SSHAgent.prototype.createConnection = createConnection; + + exports[ctor === HttpAgent ? 'SSHTTPAgent' : 'SSHTTPSAgent'] = SSHAgent; +}); + +function createConnection(options, cb) { + var srcIP = (options && options.localAddress) || this._defaultSrcIP; + var srcPort = (options && options.localPort) || 0; + var dstIP = options.host; + var dstPort = options.port; + + if (Client === undefined) + Client = __webpack_require__(597).Client; + + var client = new Client(); + var triedForward = false; + client.on('ready', () => { + client.forwardOut(srcIP, srcPort, dstIP, dstPort, (err, stream) => { + triedForward = true; + if (err) { + client.end(); + return cb(err); + } + stream.once('close', () => { + client.end(); + }); + cb(null, decorateStream(stream)); + }); + }).on('error', cb).on('close', () => { + if (!triedForward) + cb(new Error('Unexpected connection loss')); + }).connect(this._connectCfg); +} + +function noop() {} + +function decorateStream(stream) { + stream.setKeepAlive = noop; + stream.setNoDelay = noop; + stream.setTimeout = noop; + stream.ref = noop; + stream.unref = noop; + stream.destroySoon = stream.destroy; + return stream; +} + + +/***/ }), + +/***/ 315: +/***/ (function(module) { + +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }) + } + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } + } +} + + +/***/ }), + +/***/ 317: +/***/ (function(module) { + +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var w = d * 7; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isFinite(val)) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'weeks': + case 'week': + case 'w': + return n * w; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtShort(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return Math.round(ms / d) + 'd'; + } + if (msAbs >= h) { + return Math.round(ms / h) + 'h'; + } + if (msAbs >= m) { + return Math.round(ms / m) + 'm'; + } + if (msAbs >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtLong(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return plural(ms, msAbs, d, 'day'); + } + if (msAbs >= h) { + return plural(ms, msAbs, h, 'hour'); + } + if (msAbs >= m) { + return plural(ms, msAbs, m, 'minute'); + } + if (msAbs >= s) { + return plural(ms, msAbs, s, 'second'); + } + return ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, msAbs, n, name) { + var isPlural = msAbs >= n * 1.5; + return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : ''); +} + + +/***/ }), + +/***/ 334: +/***/ (function(__unusedmodule, __unusedexports, __webpack_require__) { + +const core = __webpack_require__(470); +const fs = __webpack_require__(747); +const path = __webpack_require__(622) + +// Setup Docker +const Docker = __webpack_require__(965); +var docker = new Docker({socketPath: '/var/run/docker.sock'}); + +try { + + // Get inputs + var docker_image = core.getInput('containerImage'); + var work_dir = core.getInput('directory'); + + process.chdir(work_dir); + + // Pull docker image for building + console.log("Pulling build image..."); + docker.pull(docker_image, function(err, stream) + { + + docker.modem.followProgress(stream, onFinished, onProgress); + + // Wait to run build until after pull complete + function onFinished(err, output) + { + console.log("Starting image...") + docker.run(docker_image, ['godot', '-d', '-s', '--path /builder', 'addons/gut/gut_cmdln.gd'], process.stdout, + + // Mount working directory to `/builder` + { HostConfig: { Binds: [ process.cwd() + ":/builder" ] }}, + + function (err, data, container) { + + if(err) + { + console.log(err); + } + + }) + } + function onProgress(event) {} + + }); + +} catch (error) { + core.setFailed(error.message); +} + +/***/ }), + +/***/ 357: +/***/ (function(module) { + +module.exports = require("assert"); + +/***/ }), + +/***/ 360: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var extend = __webpack_require__(255).extend, + Exec = __webpack_require__(757), + util = __webpack_require__(255); + +/** + * Represents a Container + * @param {Object} modem docker-modem + * @param {String} id Container's ID + */ +var Container = function(modem, id) { + this.modem = modem; + this.id = id; + + this.defaultOptions = { + top: {}, + start: {}, + commit: {}, + stop: {}, + pause: {}, + unpause: {}, + restart: {}, + resize: {}, + attach: {}, + remove: {}, + copy: {}, + kill: {}, + exec: {}, + rename: {}, + log: {}, + stats: {}, + getArchive: {}, + infoArchive: {}, + putArchive: {}, + update: {}, + wait: {} + }; +}; + +Container.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Inspect + * @param {Object} opts Options (optional) + * @param {Function} callback Callback, if supplied will query Docker. + * @return {Object} ID only and only if callback isn't supplied. + */ +Container.prototype.inspect = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/containers/' + this.id + '/json?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 404: 'no such container', + 500: 'server error' + } + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Rename + * @param {Object} opts Rename options + * @param {Function} callback Callback + */ +Container.prototype.rename = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.rename); + + var optsf = { + path: '/containers/' + this.id + '/rename?', + method: 'POST', + statusCodes: { + 200: true, + 204: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Update + * @param {Object} opts Update options + * @param {Function} callback Callback + */ +Container.prototype.update = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.update); + + var optsf = { + path: '/containers/' + this.id + '/update', + method: 'POST', + statusCodes: { + 200: true, + 204: true, + 400: 'bad parameter', + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Top + * @param {Object} opts like 'ps_args' (optional) + * @param {Function} callback Callback + */ +Container.prototype.top = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.top); + + var optsf = { + path: '/containers/' + this.id + '/top?', + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Containers changes + * @param {Function} callback Callback + */ +Container.prototype.changes = function(callback) { + var self = this; + var optsf = { + path: '/containers/' + this.id + '/changes', + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such container', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Checkpoints list + * @param {Object} opts List checkpoints options (optional) + * @param {Function} callback Callback + */ +Container.prototype.listCheckpoint = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/containers/' + this.id + '/checkpoints?', + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + +/** + * Delete checkpoint + * @param {Object} opts Delete checkpoint options (optional) + * @param {Function} callback Callback + */ +Container.prototype.deleteCheckpoint = function(checkpoint, opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/containers/' + this.id + '/checkpoints/' + checkpoint + '?', + method: 'DELETE', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Create checkpoint + * @param {Object} opts Create checkpoint options (optional) + * @param {Function} callback Callback + */ +Container.prototype.createCheckpoint = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/containers/' + this.id + '/checkpoints', + method: 'POST', + allowEmpty: true, + statusCodes: { + 200: true, //unofficial, but proxies may return it + 204: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + +/** + * Export + * @param {Function} callback Callback with the octet-stream. + */ +Container.prototype.export = function(callback) { + var self = this; + var optsf = { + path: '/containers/' + this.id + '/export', + method: 'GET', + isStream: true, + statusCodes: { + 200: true, + 404: 'no such container', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Start + * @param {Object} opts Container start options (optional) + * @param {Function} callback Callback + */ +Container.prototype.start = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.start); + + var optsf = { + path: '/containers/' + this.id + '/start', + method: 'POST', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 304: 'container already started', + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Pause + * @param {Object} opts Pause options (optional) + * @param {Function} callback Callback + */ +Container.prototype.pause = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.pause); + + var optsf = { + path: '/containers/' + this.id + '/pause', + method: 'POST', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Unpause + * @param {Object} opts Unpause options (optional) + * @param {Function} callback Callback + */ +Container.prototype.unpause = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.unpause); + + var optsf = { + path: '/containers/' + this.id + '/unpause', + method: 'POST', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Setup an exec call to a running container + * + * @param {object} opts + * @param {function} callback + */ +Container.prototype.exec = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.exec); + + var optsf = { + path: '/containers/' + this.id + '/exec', + method: 'POST', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 201: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(new Exec(self.modem, data.Id)); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, new Exec(self.modem, data.Id)); + }); + } +}; + +/** + * Commit + * @param {Object} opts Commit options like 'Hostname' (optional) + * @param {Function} callback Callback + */ +Container.prototype.commit = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.commit); + + args.opts.container = this.id; + + var optsf = { + path: '/commit?', + method: 'POST', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 201: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Stop + * @param {Object} opts Container stop options, like 't' (optional) + * @param {Function} callback Callback + */ +Container.prototype.stop = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.stop); + + var optsf = { + path: '/containers/' + this.id + '/stop?', + method: 'POST', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 304: 'container already stopped', + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Restart + * @param {Object} opts Container restart options, like 't' (optional) + * @param {Function} callback Callback + */ +Container.prototype.restart = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.restart); + + var optsf = { + path: '/containers/' + this.id + '/restart?', + method: 'POST', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Kill + * @param {Object} opts Container kill options, like 'signal' (optional) + * @param {Function} callback Callback + */ +Container.prototype.kill = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.kill); + + var optsf = { + path: '/containers/' + this.id + '/kill?', + method: 'POST', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Container resize + * @param {[type]} opts Resize options. (optional) + * @param {Function} callback Callback + */ +Container.prototype.resize = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.resize); + + var optsf = { + path: '/containers/' + this.id + '/resize?', + method: 'POST', + statusCodes: { + 200: true, + 400: 'bad parameter', + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Attach + * @param {Object} opts Attach options, like 'logs' (optional) + * @param {Function} callback Callback with stream. + */ +Container.prototype.attach = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.attach); + + var optsf = { + path: '/containers/' + this.id + '/attach?', + method: 'POST', + isStream: true, + hijack: args.opts.hijack, + openStdin: args.opts.stdin, + statusCodes: { + 200: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, stream) { + if (err) { + return reject(err); + } + resolve(stream); + }); + }); + } else { + this.modem.dial(optsf, function(err, stream) { + args.callback(err, stream); + }); + } +}; + +/** + * Waits for a container to end. + * @param {[type]} opts Container wait options, like condition. (optional) + * @param {Function} callback Callback + */ +Container.prototype.wait = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.wait); + + var optsf = { + path: '/containers/' + this.id + '/wait?', + method: 'POST', + statusCodes: { + 200: true, + 400: 'bad parameter', + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Removes a container + * @param {Object} opts Remove options, like 'force' (optional) + * @param {Function} callback Callback + */ +Container.prototype.remove = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.remove); + + var optsf = { + path: '/containers/' + this.id + '?', + method: 'DELETE', + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 400: 'bad parameter', + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Copy (WARNING: DEPRECATED since RAPI v1.20) + * @param {Object} opts Copy options, like 'Resource' (optional) + * @param {Function} callback Callback with stream. + */ +Container.prototype.copy = function(opts, callback) { + var self = this; + console.log('container.copy is deprecated since Docker v1.8.x'); + var args = util.processArgs(opts, callback, this.defaultOptions.copy); + + var optsf = { + path: '/containers/' + this.id + '/copy', + method: 'POST', + isStream: true, + statusCodes: { + 200: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * getArchive + * @param {Object} opts Archive options, like 'path' + * @param {Function} callback Callback with stream. + */ +Container.prototype.getArchive = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.getArchive); + + var optsf = { + path: '/containers/' + this.id + '/archive?', + method: 'GET', + isStream: true, + statusCodes: { + 200: true, + 400: 'client error, bad parameters', + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * infoArchive + * @param {Object} opts Archive options, like 'path' + * @param {Function} callback Callback with stream. + */ +Container.prototype.infoArchive = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.infoArchive); + + var optsf = { + path: '/containers/' + this.id + '/archive?', + method: 'HEAD', + isStream: true, + statusCodes: { + 200: true, + 400: 'client error, bad parameters', + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * putArchive + * @param {Object} opts Archive options, like 'path' + * @param {Function} callback Callback with stream. + */ +Container.prototype.putArchive = function(file, opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.putArchive); + + var optsf = { + path: '/containers/' + this.id + '/archive?', + method: 'PUT', + file: file, + isStream: true, + statusCodes: { + 200: true, + 400: 'client error, bad parameters', + 403: 'client error, permission denied', + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Container logs + * @param {Object} opts Logs options. (optional) + * @param {Function} callback Callback with data + */ +Container.prototype.logs = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.log); + + var optsf = { + path: '/containers/' + this.id + '/logs?', + method: 'GET', + isStream: args.opts.follow || false, + statusCodes: { + 200: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Container stats + * @param {Object} opts Stats options. (optional) + * @param {Function} callback Callback with data + */ +Container.prototype.stats = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.stats); + var isStream = true; + if (args.opts.stream === false) { + isStream = false; + } + var optsf = { + path: '/containers/' + this.id + '/stats?', + method: 'GET', + isStream: isStream, + statusCodes: { + 200: true, + 404: 'no such container', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +module.exports = Container; + + +/***/ }), + +/***/ 362: +/***/ (function(module) { + +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + EOC: 0, + Boolean: 1, + Integer: 2, + BitString: 3, + OctetString: 4, + Null: 5, + OID: 6, + ObjectDescriptor: 7, + External: 8, + Real: 9, // float + Enumeration: 10, + PDV: 11, + Utf8String: 12, + RelativeOID: 13, + Sequence: 16, + Set: 17, + NumericString: 18, + PrintableString: 19, + T61String: 20, + VideotexString: 21, + IA5String: 22, + UTCTime: 23, + GeneralizedTime: 24, + GraphicString: 25, + VisibleString: 26, + GeneralString: 28, + UniversalString: 29, + CharacterString: 30, + BMPString: 31, + Constructor: 32, + Context: 128 +}; + + +/***/ }), + +/***/ 364: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(669) +var bl = __webpack_require__(954) +var headers = __webpack_require__(154) + +var Writable = __webpack_require__(574).Writable +var PassThrough = __webpack_require__(574).PassThrough + +var noop = function () {} + +var overflow = function (size) { + size &= 511 + return size && 512 - size +} + +var emptyStream = function (self, offset) { + var s = new Source(self, offset) + s.end() + return s +} + +var mixinPax = function (header, pax) { + if (pax.path) header.name = pax.path + if (pax.linkpath) header.linkname = pax.linkpath + if (pax.size) header.size = parseInt(pax.size, 10) + header.pax = pax + return header +} + +var Source = function (self, offset) { + this._parent = self + this.offset = offset + PassThrough.call(this, { autoDestroy: false }) +} + +util.inherits(Source, PassThrough) + +Source.prototype.destroy = function (err) { + this._parent.destroy(err) +} + +var Extract = function (opts) { + if (!(this instanceof Extract)) return new Extract(opts) + Writable.call(this, opts) + + opts = opts || {} + + this._offset = 0 + this._buffer = bl() + this._missing = 0 + this._partial = false + this._onparse = noop + this._header = null + this._stream = null + this._overflow = null + this._cb = null + this._locked = false + this._destroyed = false + this._pax = null + this._paxGlobal = null + this._gnuLongPath = null + this._gnuLongLinkPath = null + + var self = this + var b = self._buffer + + var oncontinue = function () { + self._continue() + } + + var onunlock = function (err) { + self._locked = false + if (err) return self.destroy(err) + if (!self._stream) oncontinue() + } + + var onstreamend = function () { + self._stream = null + var drain = overflow(self._header.size) + if (drain) self._parse(drain, ondrain) + else self._parse(512, onheader) + if (!self._locked) oncontinue() + } + + var ondrain = function () { + self._buffer.consume(overflow(self._header.size)) + self._parse(512, onheader) + oncontinue() + } + + var onpaxglobalheader = function () { + var size = self._header.size + self._paxGlobal = headers.decodePax(b.slice(0, size)) + b.consume(size) + onstreamend() + } + + var onpaxheader = function () { + var size = self._header.size + self._pax = headers.decodePax(b.slice(0, size)) + if (self._paxGlobal) self._pax = Object.assign({}, self._paxGlobal, self._pax) + b.consume(size) + onstreamend() + } + + var ongnulongpath = function () { + var size = self._header.size + this._gnuLongPath = headers.decodeLongPath(b.slice(0, size), opts.filenameEncoding) + b.consume(size) + onstreamend() + } + + var ongnulonglinkpath = function () { + var size = self._header.size + this._gnuLongLinkPath = headers.decodeLongPath(b.slice(0, size), opts.filenameEncoding) + b.consume(size) + onstreamend() + } + + var onheader = function () { + var offset = self._offset + var header + try { + header = self._header = headers.decode(b.slice(0, 512), opts.filenameEncoding) + } catch (err) { + self.emit('error', err) + } + b.consume(512) + + if (!header) { + self._parse(512, onheader) + oncontinue() + return + } + if (header.type === 'gnu-long-path') { + self._parse(header.size, ongnulongpath) + oncontinue() + return + } + if (header.type === 'gnu-long-link-path') { + self._parse(header.size, ongnulonglinkpath) + oncontinue() + return + } + if (header.type === 'pax-global-header') { + self._parse(header.size, onpaxglobalheader) + oncontinue() + return + } + if (header.type === 'pax-header') { + self._parse(header.size, onpaxheader) + oncontinue() + return + } + + if (self._gnuLongPath) { + header.name = self._gnuLongPath + self._gnuLongPath = null + } + + if (self._gnuLongLinkPath) { + header.linkname = self._gnuLongLinkPath + self._gnuLongLinkPath = null + } + + if (self._pax) { + self._header = header = mixinPax(header, self._pax) + self._pax = null + } + + self._locked = true + + if (!header.size || header.type === 'directory') { + self._parse(512, onheader) + self.emit('entry', header, emptyStream(self, offset), onunlock) + return + } + + self._stream = new Source(self, offset) + + self.emit('entry', header, self._stream, onunlock) + self._parse(header.size, onstreamend) + oncontinue() + } + + this._onheader = onheader + this._parse(512, onheader) +} + +util.inherits(Extract, Writable) + +Extract.prototype.destroy = function (err) { + if (this._destroyed) return + this._destroyed = true + + if (err) this.emit('error', err) + this.emit('close') + if (this._stream) this._stream.emit('close') +} + +Extract.prototype._parse = function (size, onparse) { + if (this._destroyed) return + this._offset += size + this._missing = size + if (onparse === this._onheader) this._partial = false + this._onparse = onparse +} + +Extract.prototype._continue = function () { + if (this._destroyed) return + var cb = this._cb + this._cb = noop + if (this._overflow) this._write(this._overflow, undefined, cb) + else cb() +} + +Extract.prototype._write = function (data, enc, cb) { + if (this._destroyed) return + + var s = this._stream + var b = this._buffer + var missing = this._missing + if (data.length) this._partial = true + + // we do not reach end-of-chunk now. just forward it + + if (data.length < missing) { + this._missing -= data.length + this._overflow = null + if (s) return s.write(data, cb) + b.append(data) + return cb() + } + + // end-of-chunk. the parser should call cb. + + this._cb = cb + this._missing = 0 + + var overflow = null + if (data.length > missing) { + overflow = data.slice(missing) + data = data.slice(0, missing) + } + + if (s) s.end(data) + else b.append(data) + + this._overflow = overflow + this._onparse() +} + +Extract.prototype._final = function (cb) { + if (this._partial) return this.destroy(new Error('Unexpected end of data')) + cb() +} + +module.exports = Extract + + +/***/ }), + +/***/ 383: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var constants = __webpack_require__(534) +var eos = __webpack_require__(9) +var inherits = __webpack_require__(689) +var alloc = Buffer.alloc + +var Readable = __webpack_require__(574).Readable +var Writable = __webpack_require__(574).Writable +var StringDecoder = __webpack_require__(304).StringDecoder + +var headers = __webpack_require__(154) + +var DMODE = parseInt('755', 8) +var FMODE = parseInt('644', 8) + +var END_OF_TAR = alloc(1024) + +var noop = function () {} + +var overflow = function (self, size) { + size &= 511 + if (size) self.push(END_OF_TAR.slice(0, 512 - size)) +} + +function modeToType (mode) { + switch (mode & constants.S_IFMT) { + case constants.S_IFBLK: return 'block-device' + case constants.S_IFCHR: return 'character-device' + case constants.S_IFDIR: return 'directory' + case constants.S_IFIFO: return 'fifo' + case constants.S_IFLNK: return 'symlink' + } + + return 'file' +} + +var Sink = function (to) { + Writable.call(this) + this.written = 0 + this._to = to + this._destroyed = false +} + +inherits(Sink, Writable) + +Sink.prototype._write = function (data, enc, cb) { + this.written += data.length + if (this._to.push(data)) return cb() + this._to._drain = cb +} + +Sink.prototype.destroy = function () { + if (this._destroyed) return + this._destroyed = true + this.emit('close') +} + +var LinkSink = function () { + Writable.call(this) + this.linkname = '' + this._decoder = new StringDecoder('utf-8') + this._destroyed = false +} + +inherits(LinkSink, Writable) + +LinkSink.prototype._write = function (data, enc, cb) { + this.linkname += this._decoder.write(data) + cb() +} + +LinkSink.prototype.destroy = function () { + if (this._destroyed) return + this._destroyed = true + this.emit('close') +} + +var Void = function () { + Writable.call(this) + this._destroyed = false +} + +inherits(Void, Writable) + +Void.prototype._write = function (data, enc, cb) { + cb(new Error('No body allowed for this entry')) +} + +Void.prototype.destroy = function () { + if (this._destroyed) return + this._destroyed = true + this.emit('close') +} + +var Pack = function (opts) { + if (!(this instanceof Pack)) return new Pack(opts) + Readable.call(this, opts) + + this._drain = noop + this._finalized = false + this._finalizing = false + this._destroyed = false + this._stream = null +} + +inherits(Pack, Readable) + +Pack.prototype.entry = function (header, buffer, callback) { + if (this._stream) throw new Error('already piping an entry') + if (this._finalized || this._destroyed) return + + if (typeof buffer === 'function') { + callback = buffer + buffer = null + } + + if (!callback) callback = noop + + var self = this + + if (!header.size || header.type === 'symlink') header.size = 0 + if (!header.type) header.type = modeToType(header.mode) + if (!header.mode) header.mode = header.type === 'directory' ? DMODE : FMODE + if (!header.uid) header.uid = 0 + if (!header.gid) header.gid = 0 + if (!header.mtime) header.mtime = new Date() + + if (typeof buffer === 'string') buffer = Buffer.from(buffer) + if (Buffer.isBuffer(buffer)) { + header.size = buffer.length + this._encode(header) + var ok = this.push(buffer) + overflow(self, header.size) + if (ok) process.nextTick(callback) + else this._drain = callback + return new Void() + } + + if (header.type === 'symlink' && !header.linkname) { + var linkSink = new LinkSink() + eos(linkSink, function (err) { + if (err) { // stream was closed + self.destroy() + return callback(err) + } + + header.linkname = linkSink.linkname + self._encode(header) + callback() + }) + + return linkSink + } + + this._encode(header) + + if (header.type !== 'file' && header.type !== 'contiguous-file') { + process.nextTick(callback) + return new Void() + } + + var sink = new Sink(this) + + this._stream = sink + + eos(sink, function (err) { + self._stream = null + + if (err) { // stream was closed + self.destroy() + return callback(err) + } + + if (sink.written !== header.size) { // corrupting tar + self.destroy() + return callback(new Error('size mismatch')) + } + + overflow(self, header.size) + if (self._finalizing) self.finalize() + callback() + }) + + return sink +} + +Pack.prototype.finalize = function () { + if (this._stream) { + this._finalizing = true + return + } + + if (this._finalized) return + this._finalized = true + this.push(END_OF_TAR) + this.push(null) +} + +Pack.prototype.destroy = function (err) { + if (this._destroyed) return + this._destroyed = true + + if (err) this.emit('error', err) + this.emit('close') + if (this._stream && this._stream.destroy) this._stream.destroy() +} + +Pack.prototype._encode = function (header) { + if (!header.pax) { + var buf = headers.encode(header) + if (buf) { + this.push(buf) + return + } + } + this._encodePax(header) +} + +Pack.prototype._encodePax = function (header) { + var paxHeader = headers.encodePax({ + name: header.name, + linkname: header.linkname, + pax: header.pax + }) + + var newHeader = { + name: 'PaxHeader', + mode: header.mode, + uid: header.uid, + gid: header.gid, + size: paxHeader.length, + mtime: header.mtime, + type: 'pax-header', + linkname: header.linkname && 'PaxHeader', + uname: header.uname, + gname: header.gname, + devmajor: header.devmajor, + devminor: header.devminor + } + + this.push(headers.encode(newHeader)) + this.push(paxHeader) + overflow(this, paxHeader.length) + + newHeader.size = header.size + newHeader.type = header.type + this.push(headers.encode(newHeader)) +} + +Pack.prototype._read = function (n) { + var drain = this._drain + this._drain = noop + drain() +} + +module.exports = Pack + + +/***/ }), + +/***/ 393: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var Socket = __webpack_require__(631).Socket; +var EventEmitter = __webpack_require__(614).EventEmitter; +var inherits = __webpack_require__(669).inherits; +var path = __webpack_require__(622); +var fs = __webpack_require__(747); +var cp = __webpack_require__(129); + +var readUInt32BE = __webpack_require__(58).readUInt32BE; +var writeUInt32BE = __webpack_require__(58).writeUInt32BE; +var writeUInt32LE = __webpack_require__(58).writeUInt32LE; + +var REQUEST_IDENTITIES = 11; +var IDENTITIES_ANSWER = 12; +var SIGN_REQUEST = 13; +var SIGN_RESPONSE = 14; +var FAILURE = 5; + +var RE_CYGWIN_SOCK = /^\!(\d+) s ([A-Z0-9]{8}\-[A-Z0-9]{8}\-[A-Z0-9]{8}\-[A-Z0-9]{8})/; + +// Format of `//./pipe/ANYTHING`, with forward slashes and backward slashes being interchangeable +var WINDOWS_PIPE_REGEX = /^[/\\][/\\]\.[/\\]pipe[/\\].+/; + +module.exports = function(sockPath, key, keyType, data, cb) { + var sock; + var error; + var sig; + var datalen; + var keylen = 0; + var isSigning = Buffer.isBuffer(key); + var type; + var count = 0; + var siglen = 0; + var nkeys = 0; + var keys; + var comlen = 0; + var comment = false; + var accept; + var reject; + + if (typeof key === 'function' && typeof keyType === 'function') { + // agent forwarding + accept = key; + reject = keyType; + } else if (isSigning) { + keylen = key.length; + datalen = data.length; + } else { + cb = key; + key = undefined; + } + + function onconnect() { + var buf; + if (isSigning) { + /* + byte SSH2_AGENTC_SIGN_REQUEST + string key_blob + string data + uint32 flags + */ + var p = 9; + buf = Buffer.allocUnsafe(4 + 1 + 4 + keylen + 4 + datalen + 4); + writeUInt32BE(buf, buf.length - 4, 0); + buf[4] = SIGN_REQUEST; + writeUInt32BE(buf, keylen, 5); + key.copy(buf, p); + writeUInt32BE(buf, datalen, p += keylen); + data.copy(buf, p += 4); + writeUInt32BE(buf, 0, p += datalen); + sock.write(buf); + } else { + /* + byte SSH2_AGENTC_REQUEST_IDENTITIES + */ + sock.write(Buffer.from([0, 0, 0, 1, REQUEST_IDENTITIES])); + } + } + function ondata(chunk) { + for (var i = 0, len = chunk.length; i < len; ++i) { + if (type === undefined) { + // skip over packet length + if (++count === 5) { + type = chunk[i]; + count = 0; + } + } else if (type === SIGN_RESPONSE) { + /* + byte SSH2_AGENT_SIGN_RESPONSE + string signature_blob + */ + if (!sig) { + siglen <<= 8; + siglen += chunk[i]; + if (++count === 4) { + sig = Buffer.allocUnsafe(siglen); + count = 0; + } + } else { + sig[count] = chunk[i]; + if (++count === siglen) { + sock.removeAllListeners('data'); + return sock.destroy(); + } + } + } else if (type === IDENTITIES_ANSWER) { + /* + byte SSH2_AGENT_IDENTITIES_ANSWER + uint32 num_keys + + Followed by zero or more consecutive keys, encoded as: + + string public key blob + string public key comment + */ + if (keys === undefined) { + nkeys <<= 8; + nkeys += chunk[i]; + if (++count === 4) { + keys = new Array(nkeys); + count = 0; + if (nkeys === 0) { + sock.removeAllListeners('data'); + return sock.destroy(); + } + } + } else { + if (!key) { + keylen <<= 8; + keylen += chunk[i]; + if (++count === 4) { + key = Buffer.allocUnsafe(keylen); + count = 0; + } + } else if (comment === false) { + key[count] = chunk[i]; + if (++count === keylen) { + keys[nkeys - 1] = key; + keylen = 0; + count = 0; + comment = true; + if (--nkeys === 0) { + key = undefined; + sock.removeAllListeners('data'); + return sock.destroy(); + } + } + } else if (comment === true) { + comlen <<= 8; + comlen += chunk[i]; + if (++count === 4) { + count = 0; + if (comlen > 0) + comment = comlen; + else { + key = undefined; + comment = false; + } + comlen = 0; + } + } else { + // skip comments + if (++count === comment) { + comment = false; + count = 0; + key = undefined; + } + } + } + } else if (type === FAILURE) { + if (isSigning) + error = new Error('Agent unable to sign data'); + else + error = new Error('Unable to retrieve list of keys from agent'); + sock.removeAllListeners('data'); + return sock.destroy(); + } + } + } + function onerror(err) { + error = err; + } + function onclose() { + if (error) + cb(error); + else if ((isSigning && !sig) || (!isSigning && !keys)) + cb(new Error('Unexpected disconnection from agent')); + else if (isSigning && sig) + cb(undefined, sig); + else if (!isSigning && keys) + cb(undefined, keys); + } + + if (process.platform === 'win32' && !WINDOWS_PIPE_REGEX.test(sockPath)) { + if (sockPath === 'pageant') { + // Pageant (PuTTY authentication agent) + sock = new PageantSock(); + } else { + // cygwin ssh-agent instance + var triedCygpath = false; + fs.readFile(sockPath, function readCygsocket(err, data) { + if (err) { + if (triedCygpath) + return cb(new Error('Invalid cygwin unix socket path')); + // try using `cygpath` to convert a possible *nix-style path to the + // real Windows path before giving up ... + cp.exec('cygpath -w "' + sockPath + '"', + function(err, stdout, stderr) { + if (err || stdout.length === 0) + return cb(new Error('Invalid cygwin unix socket path')); + triedCygpath = true; + sockPath = stdout.toString().replace(/[\r\n]/g, ''); + fs.readFile(sockPath, readCygsocket); + }); + return; + } + + var m; + if (m = RE_CYGWIN_SOCK.exec(data.toString('ascii'))) { + var port; + var secret; + var secretbuf; + var state; + var bc = 0; + var isRetrying = false; + var inbuf = []; + var credsbuf = Buffer.allocUnsafe(12); + var i; + var j; + + // use 0 for pid, uid, and gid to ensure we get an error and also + // a valid uid and gid from cygwin so that we don't have to figure it + // out ourselves + credsbuf.fill(0); + + // parse cygwin unix socket file contents + port = parseInt(m[1], 10); + secret = m[2].replace(/\-/g, ''); + secretbuf = Buffer.allocUnsafe(16); + for (i = 0, j = 0; j < 32; ++i,j+=2) + secretbuf[i] = parseInt(secret.substring(j, j + 2), 16); + + // convert to host order (always LE for Windows) + for (i = 0; i < 16; i += 4) + writeUInt32LE(secretbuf, readUInt32BE(secretbuf, i), i); + + function _onconnect() { + bc = 0; + state = 'secret'; + sock.write(secretbuf); + } + function _ondata(data) { + bc += data.length; + if (state === 'secret') { + // the secret we sent is echoed back to us by cygwin, not sure of + // the reason for that, but we ignore it nonetheless ... + if (bc === 16) { + bc = 0; + state = 'creds'; + sock.write(credsbuf); + } + } else if (state === 'creds') { + // if this is the first attempt, make sure to gather the valid + // uid and gid for our next attempt + if (!isRetrying) + inbuf.push(data); + + if (bc === 12) { + sock.removeListener('connect', _onconnect); + sock.removeListener('data', _ondata); + sock.removeListener('close', _onclose); + if (isRetrying) { + addSockListeners(); + sock.emit('connect'); + } else { + isRetrying = true; + credsbuf = Buffer.concat(inbuf); + writeUInt32LE(credsbuf, process.pid, 0); + sock.destroy(); + tryConnect(); + } + } + } + } + function _onclose() { + cb(new Error('Problem negotiating cygwin unix socket security')); + } + function tryConnect() { + sock = new Socket(); + sock.once('connect', _onconnect); + sock.on('data', _ondata); + sock.once('close', _onclose); + sock.connect(port); + } + tryConnect(); + } else + cb(new Error('Malformed cygwin unix socket file')); + }); + return; + } + } else + sock = new Socket(); + + function addSockListeners() { + if (!accept && !reject) { + sock.once('connect', onconnect); + sock.on('data', ondata); + sock.once('error', onerror); + sock.once('close', onclose); + } else { + var chan; + sock.once('connect', function() { + chan = accept(); + var isDone = false; + function onDone() { + if (isDone) + return; + sock.destroy(); + isDone = true; + } + chan.once('end', onDone) + .once('close', onDone) + .on('data', function(data) { + sock.write(data); + }); + sock.on('data', function(data) { + chan.write(data); + }); + }); + sock.once('close', function() { + if (!chan) + reject(); + }); + } + } + addSockListeners(); + sock.connect(sockPath); +}; + + +// win32 only ------------------------------------------------------------------ +if (process.platform === 'win32') { + var RET_ERR_BADARGS = 10; + var RET_ERR_UNAVAILABLE = 11; + var RET_ERR_NOMAP = 12; + var RET_ERR_BINSTDIN = 13; + var RET_ERR_BINSTDOUT = 14; + var RET_ERR_BADLEN = 15; + + var ERROR = {}; + var EXEPATH = __webpack_require__.ab + "pagent.exe"; + ERROR[RET_ERR_BADARGS] = new Error('Invalid pagent.exe arguments'); + ERROR[RET_ERR_UNAVAILABLE] = new Error('Pageant is not running'); + ERROR[RET_ERR_NOMAP] = new Error('pagent.exe could not create an mmap'); + ERROR[RET_ERR_BINSTDIN] = new Error('pagent.exe could not set mode for stdin'); + ERROR[RET_ERR_BINSTDOUT] = new Error('pagent.exe could not set mode for stdout'); + ERROR[RET_ERR_BADLEN] = new Error('pagent.exe did not get expected input payload'); + + function PageantSock() { + this.proc = undefined; + this.buffer = null; + } + inherits(PageantSock, EventEmitter); + + PageantSock.prototype.write = function(buf) { + if (this.buffer === null) + this.buffer = buf; + else { + this.buffer = Buffer.concat([this.buffer, buf], + this.buffer.length + buf.length); + } + // Wait for at least all length bytes + if (this.buffer.length < 4) + return; + + var len = readUInt32BE(this.buffer, 0); + // Make sure we have a full message before querying pageant + if ((this.buffer.length - 4) < len) + return; + + buf = this.buffer.slice(0, 4 + len); + if (this.buffer.length > (4 + len)) + this.buffer = this.buffer.slice(4 + len); + else + this.buffer = null; + + var self = this; + var proc; + var hadError = false; + proc = this.proc = cp.spawn(__webpack_require__.ab + "pagent.exe", [ buf.length ]); + proc.stdout.on('data', function(data) { + self.emit('data', data); + }); + proc.once('error', function(err) { + if (!hadError) { + hadError = true; + self.emit('error', err); + } + }); + proc.once('close', function(code) { + self.proc = undefined; + if (ERROR[code] && !hadError) { + hadError = true; + self.emit('error', ERROR[code]); + } + self.emit('close', hadError); + }); + proc.stdin.end(buf); + }; + PageantSock.prototype.end = PageantSock.prototype.destroy = function() { + this.buffer = null; + if (this.proc) { + this.proc.kill(); + this.proc = undefined; + } + }; + PageantSock.prototype.connect = function() { + this.emit('connect'); + }; +} + + +/***/ }), + +/***/ 394: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +exports.extract = __webpack_require__(364) +exports.pack = __webpack_require__(383) + + +/***/ }), + +/***/ 413: +/***/ (function(module) { + +module.exports = require("stream"); + +/***/ }), + +/***/ 417: +/***/ (function(module) { + +module.exports = require("crypto"); + +/***/ }), + +/***/ 427: +/***/ (function(module, __unusedexports, __webpack_require__) { + +module.exports = __webpack_require__(413); + + +/***/ }), + +/***/ 431: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const os = __importStar(__webpack_require__(87)); +/** + * Commands + * + * Command Format: + * ::name key=value,key=value::message + * + * Examples: + * ::warning::This is the message + * ::set-env name=MY_VAR::some value + */ +function issueCommand(command, properties, message) { + const cmd = new Command(command, properties, message); + process.stdout.write(cmd.toString() + os.EOL); +} +exports.issueCommand = issueCommand; +function issue(name, message = '') { + issueCommand(name, {}, message); +} +exports.issue = issue; +const CMD_STRING = '::'; +class Command { + constructor(command, properties, message) { + if (!command) { + command = 'missing.command'; + } + this.command = command; + this.properties = properties; + this.message = message; + } + toString() { + let cmdStr = CMD_STRING + this.command; + if (this.properties && Object.keys(this.properties).length > 0) { + cmdStr += ' '; + let first = true; + for (const key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + const val = this.properties[key]; + if (val) { + if (first) { + first = false; + } + else { + cmdStr += ','; + } + cmdStr += `${key}=${escapeProperty(val)}`; + } + } + } + } + cmdStr += `${CMD_STRING}${escapeData(this.message)}`; + return cmdStr; + } +} +/** + * Sanitizes an input into a string so it can be passed into issueCommand safely + * @param input input to sanitize into a string + */ +function toCommandValue(input) { + if (input === null || input === undefined) { + return ''; + } + else if (typeof input === 'string' || input instanceof String) { + return input; + } + return JSON.stringify(input); +} +exports.toCommandValue = toCommandValue; +function escapeData(s) { + return toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A'); +} +function escapeProperty(s) { + return toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C'); +} +//# sourceMappingURL=command.js.map + +/***/ }), + +/***/ 453: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var once = __webpack_require__(49) +var eos = __webpack_require__(9) +var fs = __webpack_require__(747) // we only need fs to get the ReadStream and WriteStream prototypes + +var noop = function () {} +var ancient = /^v?\.0/.test(process.version) + +var isFn = function (fn) { + return typeof fn === 'function' +} + +var isFS = function (stream) { + if (!ancient) return false // newer node version do not need to care about fs is a special way + if (!fs) return false // browser + return (stream instanceof (fs.ReadStream || noop) || stream instanceof (fs.WriteStream || noop)) && isFn(stream.close) +} + +var isRequest = function (stream) { + return stream.setHeader && isFn(stream.abort) +} + +var destroyer = function (stream, reading, writing, callback) { + callback = once(callback) + + var closed = false + stream.on('close', function () { + closed = true + }) + + eos(stream, {readable: reading, writable: writing}, function (err) { + if (err) return callback(err) + closed = true + callback() + }) + + var destroyed = false + return function (err) { + if (closed) return + if (destroyed) return + destroyed = true + + if (isFS(stream)) return stream.close(noop) // use close for fs streams to avoid fd leaks + if (isRequest(stream)) return stream.abort() // request.destroy just do .end - .abort is what we want + + if (isFn(stream.destroy)) return stream.destroy() + + callback(err || new Error('stream was destroyed')) + } +} + +var call = function (fn) { + fn() +} + +var pipe = function (from, to) { + return from.pipe(to) +} + +var pump = function () { + var streams = Array.prototype.slice.call(arguments) + var callback = isFn(streams[streams.length - 1] || noop) && streams.pop() || noop + + if (Array.isArray(streams[0])) streams = streams[0] + if (streams.length < 2) throw new Error('pump requires two streams per minimum') + + var error + var destroys = streams.map(function (stream, i) { + var reading = i < streams.length - 1 + var writing = i > 0 + return destroyer(stream, reading, writing, function (err) { + if (!error) error = err + if (err) destroys.forEach(call) + if (reading) return + destroys.forEach(call) + callback(error) + }) + }) + + return streams.reduce(pipe) +} + +module.exports = pump + + +/***/ }), + +/***/ 458: +/***/ (function(module, __unusedexports, __webpack_require__) { + +/* + Based heavily on the Streaming Boyer-Moore-Horspool C++ implementation + by Hongli Lai at: https://github.com/FooBarWidget/boyer-moore-horspool +*/ +var EventEmitter = __webpack_require__(614).EventEmitter, + inherits = __webpack_require__(669).inherits; + +function jsmemcmp(buf1, pos1, buf2, pos2, num) { + for (var i = 0; i < num; ++i, ++pos1, ++pos2) + if (buf1[pos1] !== buf2[pos2]) + return false; + return true; +} + +function SBMH(needle) { + if (typeof needle === 'string') + needle = new Buffer(needle); + var i, j, needle_len = needle.length; + + this.maxMatches = Infinity; + this.matches = 0; + + this._occ = new Array(256); + this._lookbehind_size = 0; + this._needle = needle; + this._bufpos = 0; + + this._lookbehind = new Buffer(needle_len); + + // Initialize occurrence table. + for (j = 0; j < 256; ++j) + this._occ[j] = needle_len; + + // Populate occurrence table with analysis of the needle, + // ignoring last letter. + if (needle_len >= 1) { + for (i = 0; i < needle_len - 1; ++i) + this._occ[needle[i]] = needle_len - 1 - i; + } +} +inherits(SBMH, EventEmitter); + +SBMH.prototype.reset = function() { + this._lookbehind_size = 0; + this.matches = 0; + this._bufpos = 0; +}; + +SBMH.prototype.push = function(chunk, pos) { + var r, chlen; + if (!Buffer.isBuffer(chunk)) + chunk = new Buffer(chunk, 'binary'); + chlen = chunk.length; + this._bufpos = pos || 0; + while (r !== chlen && this.matches < this.maxMatches) + r = this._sbmh_feed(chunk); + return r; +}; + +SBMH.prototype._sbmh_feed = function(data) { + var len = data.length, needle = this._needle, needle_len = needle.length; + + // Positive: points to a position in `data` + // pos == 3 points to data[3] + // Negative: points to a position in the lookbehind buffer + // pos == -2 points to lookbehind[lookbehind_size - 2] + var pos = -this._lookbehind_size, + last_needle_char = needle[needle_len - 1], + occ = this._occ, + lookbehind = this._lookbehind; + + if (pos < 0) { + // Lookbehind buffer is not empty. Perform Boyer-Moore-Horspool + // search with character lookup code that considers both the + // lookbehind buffer and the current round's haystack data. + // + // Loop until + // there is a match. + // or until + // we've moved past the position that requires the + // lookbehind buffer. In this case we switch to the + // optimized loop. + // or until + // the character to look at lies outside the haystack. + while (pos < 0 && pos <= len - needle_len) { + var ch = this._sbmh_lookup_char(data, pos + needle_len - 1); + + if (ch === last_needle_char + && this._sbmh_memcmp(data, pos, needle_len - 1)) { + this._lookbehind_size = 0; + ++this.matches; + if (pos > -this._lookbehind_size) + this.emit('info', true, lookbehind, 0, this._lookbehind_size + pos); + else + this.emit('info', true); + + this._bufpos = pos + needle_len; + return pos + needle_len; + } else + pos += occ[ch]; + } + + // No match. + + if (pos < 0) { + // There's too few data for Boyer-Moore-Horspool to run, + // so let's use a different algorithm to skip as much as + // we can. + // Forward pos until + // the trailing part of lookbehind + data + // looks like the beginning of the needle + // or until + // pos == 0 + while (pos < 0 && !this._sbmh_memcmp(data, pos, len - pos)) + pos++; + } + + if (pos >= 0) { + // Discard lookbehind buffer. + this.emit('info', false, lookbehind, 0, this._lookbehind_size); + this._lookbehind_size = 0; + } else { + // Cut off part of the lookbehind buffer that has + // been processed and append the entire haystack + // into it. + var bytesToCutOff = this._lookbehind_size + pos; + + if (bytesToCutOff > 0) { + // The cut off data is guaranteed not to contain the needle. + this.emit('info', false, lookbehind, 0, bytesToCutOff); + } + + lookbehind.copy(lookbehind, 0, bytesToCutOff, + this._lookbehind_size - bytesToCutOff); + this._lookbehind_size -= bytesToCutOff; + + data.copy(lookbehind, this._lookbehind_size); + this._lookbehind_size += len; + + this._bufpos = len; + return len; + } + } + + if (pos >= 0) + pos += this._bufpos; + + // Lookbehind buffer is now empty. Perform Boyer-Moore-Horspool + // search with optimized character lookup code that only considers + // the current round's haystack data. + while (pos <= len - needle_len) { + var ch = data[pos + needle_len - 1]; + + if (ch === last_needle_char + && data[pos] === needle[0] + && jsmemcmp(needle, 0, data, pos, needle_len - 1)) { + ++this.matches; + if (pos > 0) + this.emit('info', true, data, this._bufpos, pos); + else + this.emit('info', true); + + this._bufpos = pos + needle_len; + return pos + needle_len; + } else + pos += occ[ch]; + } + + // There was no match. If there's trailing haystack data that we cannot + // match yet using the Boyer-Moore-Horspool algorithm (because the trailing + // data is less than the needle size) then match using a modified + // algorithm that starts matching from the beginning instead of the end. + // Whatever trailing data is left after running this algorithm is added to + // the lookbehind buffer. + if (pos < len) { + while (pos < len && (data[pos] !== needle[0] + || !jsmemcmp(data, pos, needle, 0, len - pos))) { + ++pos; + } + if (pos < len) { + data.copy(lookbehind, 0, pos, pos + (len - pos)); + this._lookbehind_size = len - pos; + } + } + + // Everything until pos is guaranteed not to contain needle data. + if (pos > 0) + this.emit('info', false, data, this._bufpos, pos < len ? pos : len); + + this._bufpos = len; + return len; +}; + +SBMH.prototype._sbmh_lookup_char = function(data, pos) { + if (pos < 0) + return this._lookbehind[this._lookbehind_size + pos]; + else + return data[pos]; +} + +SBMH.prototype._sbmh_memcmp = function(data, pos, len) { + var i = 0; + + while (i < len) { + if (this._sbmh_lookup_char(data, pos + i) === this._needle[i]) + ++i; + else + return false; + } + return true; +} + +module.exports = SBMH; + + +/***/ }), + +/***/ 470: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const command_1 = __webpack_require__(431); +const os = __importStar(__webpack_require__(87)); +const path = __importStar(__webpack_require__(622)); +/** + * The code to exit an action + */ +var ExitCode; +(function (ExitCode) { + /** + * A code indicating that the action was successful + */ + ExitCode[ExitCode["Success"] = 0] = "Success"; + /** + * A code indicating that the action was a failure + */ + ExitCode[ExitCode["Failure"] = 1] = "Failure"; +})(ExitCode = exports.ExitCode || (exports.ExitCode = {})); +//----------------------------------------------------------------------- +// Variables +//----------------------------------------------------------------------- +/** + * Sets env variable for this action and future actions in the job + * @param name the name of the variable to set + * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function exportVariable(name, val) { + const convertedVal = command_1.toCommandValue(val); + process.env[name] = convertedVal; + command_1.issueCommand('set-env', { name }, convertedVal); +} +exports.exportVariable = exportVariable; +/** + * Registers a secret which will get masked from logs + * @param secret value of the secret + */ +function setSecret(secret) { + command_1.issueCommand('add-mask', {}, secret); +} +exports.setSecret = setSecret; +/** + * Prepends inputPath to the PATH (for this action and future actions) + * @param inputPath + */ +function addPath(inputPath) { + command_1.issueCommand('add-path', {}, inputPath); + process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; +} +exports.addPath = addPath; +/** + * Gets the value of an input. The value is also trimmed. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string + */ +function getInput(name, options) { + const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; + if (options && options.required && !val) { + throw new Error(`Input required and not supplied: ${name}`); + } + return val.trim(); +} +exports.getInput = getInput; +/** + * Sets the value of an output. + * + * @param name name of the output to set + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function setOutput(name, value) { + command_1.issueCommand('set-output', { name }, value); +} +exports.setOutput = setOutput; +/** + * Enables or disables the echoing of commands into stdout for the rest of the step. + * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * + */ +function setCommandEcho(enabled) { + command_1.issue('echo', enabled ? 'on' : 'off'); +} +exports.setCommandEcho = setCommandEcho; +//----------------------------------------------------------------------- +// Results +//----------------------------------------------------------------------- +/** + * Sets the action status to failed. + * When the action exits it will be with an exit code of 1 + * @param message add error issue message + */ +function setFailed(message) { + process.exitCode = ExitCode.Failure; + error(message); +} +exports.setFailed = setFailed; +//----------------------------------------------------------------------- +// Logging Commands +//----------------------------------------------------------------------- +/** + * Gets whether Actions Step Debug is on or not + */ +function isDebug() { + return process.env['RUNNER_DEBUG'] === '1'; +} +exports.isDebug = isDebug; +/** + * Writes debug message to user log + * @param message debug message + */ +function debug(message) { + command_1.issueCommand('debug', {}, message); +} +exports.debug = debug; +/** + * Adds an error issue + * @param message error issue message. Errors will be converted to string via toString() + */ +function error(message) { + command_1.issue('error', message instanceof Error ? message.toString() : message); +} +exports.error = error; +/** + * Adds an warning issue + * @param message warning issue message. Errors will be converted to string via toString() + */ +function warning(message) { + command_1.issue('warning', message instanceof Error ? message.toString() : message); +} +exports.warning = warning; +/** + * Writes info to log with console.log. + * @param message info message + */ +function info(message) { + process.stdout.write(message + os.EOL); +} +exports.info = info; +/** + * Begin an output group. + * + * Output until the next `groupEnd` will be foldable in this group + * + * @param name The name of the output group + */ +function startGroup(name) { + command_1.issue('group', name); +} +exports.startGroup = startGroup; +/** + * End an output group. + */ +function endGroup() { + command_1.issue('endgroup'); +} +exports.endGroup = endGroup; +/** + * Wrap an asynchronous function call in a group. + * + * Returns the same type as the function itself. + * + * @param name The name of the group + * @param fn The function to wrap in the group + */ +function group(name, fn) { + return __awaiter(this, void 0, void 0, function* () { + startGroup(name); + let result; + try { + result = yield fn(); + } + finally { + endGroup(); + } + return result; + }); +} +exports.group = group; +//----------------------------------------------------------------------- +// Wrapper action state +//----------------------------------------------------------------------- +/** + * Saves state for current action, the state can only be retrieved by this action's post job execution. + * + * @param name name of the state to store + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function saveState(name, value) { + command_1.issueCommand('save-state', { name }, value); +} +exports.saveState = saveState; +/** + * Gets the value of an state set by this action's main execution. + * + * @param name name of the state to get + * @returns string + */ +function getState(name) { + return process.env[`STATE_${name}`] || ''; +} +exports.getState = getState; +//# sourceMappingURL=core.js.map + +/***/ }), + +/***/ 479: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents a volume + * @param {Object} modem docker-modem + * @param {String} name Volume's name + */ +var Volume = function(modem, name) { + this.modem = modem; + this.name = name; +}; + +Volume.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Inspect + * @param {Function} callback Callback, if specified Docker will be queried. + * @return {Object} Name only if callback isn't specified. + */ +Volume.prototype.inspect = function(callback) { + var self = this; + + var optsf = { + path: '/volumes/' + this.name, + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such volume', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Removes the volume + * @param {[Object]} opts Remove options (optional) + * @param {Function} callback Callback + */ +Volume.prototype.remove = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/volumes/' + this.name, + method: 'DELETE', + statusCodes: { + 204: true, + 404: 'no such volume', + 409: 'conflict', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +module.exports = Volume; + + +/***/ }), + +/***/ 486: +/***/ (function(module, __unusedexports, __webpack_require__) { + + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + */ + +function setup(env) { + createDebug.debug = createDebug; + createDebug.default = createDebug; + createDebug.coerce = coerce; + createDebug.disable = disable; + createDebug.enable = enable; + createDebug.enabled = enabled; + createDebug.humanize = __webpack_require__(317); + + Object.keys(env).forEach(key => { + createDebug[key] = env[key]; + }); + + /** + * Active `debug` instances. + */ + createDebug.instances = []; + + /** + * The currently active debug mode names, and names to skip. + */ + + createDebug.names = []; + createDebug.skips = []; + + /** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + createDebug.formatters = {}; + + /** + * Selects a color for a debug namespace + * @param {String} namespace The namespace string for the for the debug instance to be colored + * @return {Number|String} An ANSI color code for the given namespace + * @api private + */ + function selectColor(namespace) { + let hash = 0; + + for (let i = 0; i < namespace.length; i++) { + hash = ((hash << 5) - hash) + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; + } + createDebug.selectColor = selectColor; + + /** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + function createDebug(namespace) { + let prevTime; + + function debug(...args) { + // Disabled? + if (!debug.enabled) { + return; + } + + const self = debug; + + // Set `diff` timestamp + const curr = Number(new Date()); + const ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + args[0] = createDebug.coerce(args[0]); + + if (typeof args[0] !== 'string') { + // Anything else let's inspect with %O + args.unshift('%O'); + } + + // Apply any `formatters` transformations + let index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { + // If we encounter an escaped % then don't increase the array index + if (match === '%%') { + return match; + } + index++; + const formatter = createDebug.formatters[format]; + if (typeof formatter === 'function') { + const val = args[index]; + match = formatter.call(self, val); + + // Now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + // Apply env-specific formatting (colors, etc.) + createDebug.formatArgs.call(self, args); + + const logFn = self.log || createDebug.log; + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = createDebug.enabled(namespace); + debug.useColors = createDebug.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; + debug.extend = extend; + // Debug.formatArgs = formatArgs; + // debug.rawLog = rawLog; + + // env-specific initialization logic for debug instances + if (typeof createDebug.init === 'function') { + createDebug.init(debug); + } + + createDebug.instances.push(debug); + + return debug; + } + + function destroy() { + const index = createDebug.instances.indexOf(this); + if (index !== -1) { + createDebug.instances.splice(index, 1); + return true; + } + return false; + } + + function extend(namespace, delimiter) { + const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); + newDebug.log = this.log; + return newDebug; + } + + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + function enable(namespaces) { + createDebug.save(namespaces); + + createDebug.names = []; + createDebug.skips = []; + + let i; + const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + const len = split.length; + + for (i = 0; i < len; i++) { + if (!split[i]) { + // ignore empty strings + continue; + } + + namespaces = split[i].replace(/\*/g, '.*?'); + + if (namespaces[0] === '-') { + createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + createDebug.names.push(new RegExp('^' + namespaces + '$')); + } + } + + for (i = 0; i < createDebug.instances.length; i++) { + const instance = createDebug.instances[i]; + instance.enabled = createDebug.enabled(instance.namespace); + } + } + + /** + * Disable debug output. + * + * @return {String} namespaces + * @api public + */ + function disable() { + const namespaces = [ + ...createDebug.names.map(toNamespace), + ...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace) + ].join(','); + createDebug.enable(''); + return namespaces; + } + + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + + let i; + let len; + + for (i = 0, len = createDebug.skips.length; i < len; i++) { + if (createDebug.skips[i].test(name)) { + return false; + } + } + + for (i = 0, len = createDebug.names.length; i < len; i++) { + if (createDebug.names[i].test(name)) { + return true; + } + } + + return false; + } + + /** + * Convert regexp to namespace + * + * @param {RegExp} regxep + * @return {String} namespace + * @api private + */ + function toNamespace(regexp) { + return regexp.toString() + .substring(2, regexp.toString().length - 2) + .replace(/\.\*\?$/, '*'); + } + + /** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + function coerce(val) { + if (val instanceof Error) { + return val.stack || val.message; + } + return val; + } + + createDebug.enable(createDebug.load()); + + return createDebug; +} + +module.exports = setup; + + +/***/ }), + +/***/ 498: +/***/ (function(module, exports, __webpack_require__) { + +//Based on follow-redirects v0.0.x + +var nativeHttps = __webpack_require__(211), + nativeHttp = __webpack_require__(605), + url = __webpack_require__(835), + utils = __webpack_require__(75); + +var maxRedirects = module.exports.maxRedirects = 5; + +var protocols = { + https: nativeHttps, + http: nativeHttp +}; + +for (var protocol in protocols) { + var h = function() {}; + h.prototype = protocols[protocol]; + h = new h(); + + h.request = function(h) { + return function(options, callback, redirectOptions) { + + redirectOptions = redirectOptions || {}; + + var max = (typeof options === 'object' && 'maxRedirects' in options) ? options.maxRedirects : exports.maxRedirects; + + var redirect = utils.extend({ + count: 0, + max: max, + clientRequest: null, + userCallback: callback + }, redirectOptions); + + if (redirect.count > redirect.max) { + var err = new Error('Max redirects exceeded. To allow more redirects, pass options.maxRedirects property.'); + redirect.clientRequest.emit('error', err); + return redirect.clientRequest; + } + + redirect.count++; + + var reqUrl; + if (typeof options === 'string') { + reqUrl = options; + } else { + reqUrl = url.format(utils.extend({ + protocol: protocol + }, options)); + } + + var clientRequest = Object.getPrototypeOf(h).request(options, redirectCallback(reqUrl, redirect)); + + if (!redirect.clientRequest) redirect.clientRequest = clientRequest; + + function redirectCallback(reqUrl, redirect) { + return function(res) { + if (res.statusCode < 300 || res.statusCode > 399) { + return redirect.userCallback(res); + } + + if (!('location' in res.headers)) { + return redirect.userCallback(res); + } + + var redirectUrl = url.resolve(reqUrl, res.headers.location); + + var proto = url.parse(redirectUrl).protocol; + proto = proto.substr(0, proto.length - 1); + return module.exports[proto].get(redirectUrl, redirectCallback(reqUrl, redirect), redirect); + }; + } + + return clientRequest; + }; + }(h); + + // see https://github.com/joyent/node/blob/master/lib/http.js#L1623 + h.get = function(h) { + return function(options, cb, redirectOptions) { + var req = h.request(options, cb, redirectOptions); + req.end(); + return req; + }; + }(h); + + module.exports[protocol] = h; +} + + +/***/ }), + +/***/ 516: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var path = __webpack_require__(622); +var fs = __webpack_require__(747); +var _0777 = parseInt('0777', 8); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, opts, f, made) { + if (typeof opts === 'function') { + f = opts; + opts = {}; + } + else if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + p = path.resolve(p); + + xfs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), opts, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, opts, cb, made); + }); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + xfs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} + +mkdirP.sync = function sync (p, opts, made) { + if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + p = path.resolve(p); + + try { + xfs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), opts, made); + sync(p, opts, made); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = xfs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } + + return made; +}; + + +/***/ }), + +/***/ 523: +/***/ (function(module) { + +module.exports = { + readUInt32BE: function readUInt32BE(buf, offset) { + return buf[offset++] * 16777216 + + buf[offset++] * 65536 + + buf[offset++] * 256 + + buf[offset]; + }, + writeUInt32BE: function writeUInt32BE(buf, value, offset) { + buf[offset++] = (value >>> 24); + buf[offset++] = (value >>> 16); + buf[offset++] = (value >>> 8); + buf[offset++] = value; + return offset; + }, + writeUInt32LE: function writeUInt32LE(buf, value, offset) { + buf[offset++] = value; + buf[offset++] = (value >>> 8); + buf[offset++] = (value >>> 16); + buf[offset++] = (value >>> 24); + return offset; + } +}; + + +/***/ }), + +/***/ 534: +/***/ (function(module, __unusedexports, __webpack_require__) { + +module.exports = __webpack_require__(747).constants || __webpack_require__(619) + + +/***/ }), + +/***/ 546: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents a plugin + * @param {Object} modem docker-modem + * @param {String} name Plugin's name + */ +var Plugin = function(modem, name, remote) { + this.modem = modem; + this.name = name; + this.remote = remote || name; +}; + +Plugin.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Inspect + * @param {Function} callback Callback, if specified Docker will be queried. + * @return {Object} Name only if callback isn't specified. + */ +Plugin.prototype.inspect = function(callback) { + var self = this; + + var optsf = { + path: '/plugins/' + this.name, + method: 'GET', + statusCodes: { + 200: true, + 404: 'plugin is not installed', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Removes the plugin + * @param {[Object]} opts Remove options (optional) + * @param {Function} callback Callback + */ +Plugin.prototype.remove = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/plugins/' + this.name + '?', + method: 'DELETE', + statusCodes: { + 200: true, + 404: 'plugin is not installed', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, data); + }); + } +}; + +/** + * get privileges + * @param {Function} callback Callback + * @return {Object} Name only if callback isn't specified. + */ +Plugin.prototype.privileges = function(callback) { + var self = this; + var optsf = { + path: '/plugins/privileges?', + method: 'GET', + options: { + 'remote': this.remote + }, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + + +/** + * Installs a new plugin + * @param {Object} opts Create options + * @param {Function} callback Callback + */ +Plugin.prototype.pull = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + if(args.opts._query && !args.opts._query.name) { + args.opts._query.name = this.name; + } + if(args.opts._query && !args.opts._query.remote) { + args.opts._query.remote = this.remote; + } + + var optsf = { + path: '/plugins/pull?', + method: 'POST', + isStream: true, + options: args.opts, + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 500: 'server error' + } + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + +/** + * Enable + * @param {Object} opts Plugin enable options (optional) + * @param {Function} callback Callback + */ +Plugin.prototype.enable = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/plugins/' + this.name + '/enable?', + method: 'POST', + statusCodes: { + 200: true, + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Disable + * @param {Object} opts Plugin disable options (optional) + * @param {Function} callback Callback + */ +Plugin.prototype.disable = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/plugins/' + this.name + '/disable', + method: 'POST', + statusCodes: { + 200: true, + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Push + * @param {Object} opts Plugin push options (optional) + * @param {Function} callback Callback + */ +Plugin.prototype.push = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/plugins/' + this.name + '/push', + method: 'POST', + statusCodes: { + 200: true, + 404: 'plugin not installed', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * COnfigure + * @param {Object} opts Plugin configure options (optional) + * @param {Function} callback Callback + */ +Plugin.prototype.configure = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/plugins/' + this.name + '/set', + method: 'POST', + statusCodes: { + 200: true, + 404: 'plugin not installed', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + +/** + * Upgrade plugin + * + * @param {object} auth + * @param {object} opts + * @param {function} callback + */ +Plugin.prototype.upgrade = function(auth, opts, callback) { + var self = this; + if (!callback && typeof opts === 'function') { + callback = opts; + opts = auth; + auth = opts.authconfig || undefined; + } + + var optsf = { + path: '/plugins/' + this.name + '/upgrade?', + method: 'POST', + statusCodes: { + 200: true, + 204: true, + 404: 'plugin not installed', + 500: 'server error' + }, + authconfig: auth, + options: opts + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + + +module.exports = Plugin; + + +/***/ }), + +/***/ 563: +/***/ (function(module) { + +"use strict"; + + +const codes = {}; + +function createErrorType(code, message, Base) { + if (!Base) { + Base = Error + } + + function getMessage (arg1, arg2, arg3) { + if (typeof message === 'string') { + return message + } else { + return message(arg1, arg2, arg3) + } + } + + class NodeError extends Base { + constructor (arg1, arg2, arg3) { + super(getMessage(arg1, arg2, arg3)); + } + } + + NodeError.prototype.name = Base.name; + NodeError.prototype.code = code; + + codes[code] = NodeError; +} + +// https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js +function oneOf(expected, thing) { + if (Array.isArray(expected)) { + const len = expected.length; + expected = expected.map((i) => String(i)); + if (len > 2) { + return `one of ${thing} ${expected.slice(0, len - 1).join(', ')}, or ` + + expected[len - 1]; + } else if (len === 2) { + return `one of ${thing} ${expected[0]} or ${expected[1]}`; + } else { + return `of ${thing} ${expected[0]}`; + } + } else { + return `of ${thing} ${String(expected)}`; + } +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith +function startsWith(str, search, pos) { + return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith +function endsWith(str, search, this_len) { + if (this_len === undefined || this_len > str.length) { + this_len = str.length; + } + return str.substring(this_len - search.length, this_len) === search; +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes +function includes(str, search, start) { + if (typeof start !== 'number') { + start = 0; + } + + if (start + search.length > str.length) { + return false; + } else { + return str.indexOf(search, start) !== -1; + } +} + +createErrorType('ERR_INVALID_OPT_VALUE', function (name, value) { + return 'The value "' + value + '" is invalid for option "' + name + '"' +}, TypeError); +createErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) { + // determiner: 'must be' or 'must not be' + let determiner; + if (typeof expected === 'string' && startsWith(expected, 'not ')) { + determiner = 'must not be'; + expected = expected.replace(/^not /, ''); + } else { + determiner = 'must be'; + } + + let msg; + if (endsWith(name, ' argument')) { + // For cases like 'first argument' + msg = `The ${name} ${determiner} ${oneOf(expected, 'type')}`; + } else { + const type = includes(name, '.') ? 'property' : 'argument'; + msg = `The "${name}" ${type} ${determiner} ${oneOf(expected, 'type')}`; + } + + msg += `. Received type ${typeof actual}`; + return msg; +}, TypeError); +createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF'); +createErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) { + return 'The ' + name + ' method is not implemented' +}); +createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close'); +createErrorType('ERR_STREAM_DESTROYED', function (name) { + return 'Cannot call ' + name + ' after a stream was destroyed'; +}); +createErrorType('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times'); +createErrorType('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable'); +createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end'); +createErrorType('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError); +createErrorType('ERR_UNKNOWN_ENCODING', function (arg) { + return 'Unknown encoding: ' + arg +}, TypeError); +createErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event'); + +module.exports.codes = codes; + + +/***/ }), + +/***/ 574: +/***/ (function(module, exports, __webpack_require__) { + +var Stream = __webpack_require__(413); +if (process.env.READABLE_STREAM === 'disable' && Stream) { + module.exports = Stream.Readable; + Object.assign(module.exports, Stream); + module.exports.Stream = Stream; +} else { + exports = module.exports = __webpack_require__(226); + exports.Stream = Stream || exports; + exports.Readable = exports; + exports.Writable = __webpack_require__(241); + exports.Duplex = __webpack_require__(831); + exports.Transform = __webpack_require__(925); + exports.PassThrough = __webpack_require__(882); + exports.finished = __webpack_require__(740); + exports.pipeline = __webpack_require__(238); +} + + +/***/ }), + +/***/ 584: +/***/ (function(module) { + +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + + newInvalidAsn1Error: function (msg) { + var e = new Error(); + e.name = 'InvalidAsn1Error'; + e.message = msg || ''; + return e; + } + +}; + + +/***/ }), + +/***/ 588: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents an image + * @param {Object} modem docker-modem + * @param {String} name Image's name + */ +var Image = function(modem, name) { + this.modem = modem; + this.name = name; +}; + +Image.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Inspect + * @param {Function} callback Callback, if specified Docker will be queried. + * @return {Object} Name only if callback isn't specified. + */ +Image.prototype.inspect = function(callback) { + var self = this; + + var opts = { + path: '/images/' + this.name + '/json', + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such image', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(opts, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(opts, function(err, data) { + if (err) return callback(err, data); + callback(err, data); + }); + } +}; + +/** + * Distribution + * @param {Object} opts + * @param {Function} callback Callback, if specified Docker will be queried. + * @return {Object} Name only if callback isn't specified. + */ +Image.prototype.distribution = function(opts, callback) { + var args = util.processArgs(opts, callback); + var self = this; + + var fopts = { + path: '/distribution/' + this.name + '/json', + method: 'GET', + statusCodes: { + 200: true, + 401: 'no such image', + 500: 'server error' + }, + authconfig: (args.opts) ? args.opts.authconfig : undefined + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(fopts, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(fopts, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, data); + }); + } +}; + +/** + * History + * @param {Function} callback Callback + */ +Image.prototype.history = function(callback) { + var self = this; + var opts = { + path: '/images/' + this.name + '/history', + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such image', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(opts, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(opts, function(err, data) { + if (err) return callback(err, data); + callback(err, data); + }); + } +}; + +/** + * Get + * @param {Function} callback Callback with data stream. + */ +Image.prototype.get = function(callback) { + var self = this; + var opts = { + path: '/images/' + this.name + '/get', + method: 'GET', + isStream: true, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(opts, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(opts, function(err, data) { + if (err) return callback(err, data); + callback(err, data); + }); + } +}; + +/** + * Push + * @param {Object} opts Push options, like 'registry' (optional) + * @param {Function} callback Callback with stream. + * @param {Object} auth Registry authentication + */ +Image.prototype.push = function(opts, callback, auth) { + var self = this; + var args = util.processArgs(opts, callback); + var isStream = true; + if (args.opts.stream === false) { + isStream = false; + } + var optsf = { + path: '/images/' + this.name + '/push?', + method: 'POST', + options: args.opts, + authconfig: args.opts.authconfig || auth, + isStream: isStream, + statusCodes: { + 200: true, + 404: 'no such image', + 500: 'server error' + } + }; + + delete optsf.options.authconfig; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Tag + * @param {Object} opts Tag options, like 'repo' (optional) + * @param {Function} callback Callback + */ +Image.prototype.tag = function(opts, callback) { + var self = this; + var optsf = { + path: '/images/' + this.name + '/tag?', + method: 'POST', + options: opts, + statusCodes: { + 200: true, // unofficial, but proxies may return it + 201: true, + 400: 'bad parameter', + 404: 'no such image', + 409: 'conflict', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Removes the image + * @param {[Object]} opts Remove options (optional) + * @param {Function} callback Callback + */ +Image.prototype.remove = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + + var optsf = { + path: '/images/' + this.name + '?', + method: 'DELETE', + statusCodes: { + 200: true, + 404: 'no such image', + 409: 'conflict', + 500: 'server error' + }, + options: args.opts + }; + + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +module.exports = Image; + + +/***/ }), + +/***/ 597: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var crypto = __webpack_require__(417); +var Socket = __webpack_require__(631).Socket; +var dnsLookup = __webpack_require__(881).lookup; +var EventEmitter = __webpack_require__(614).EventEmitter; +var inherits = __webpack_require__(669).inherits; +var HASHES = crypto.getHashes(); + +var ssh2_streams = __webpack_require__(139); +var SSH2Stream = ssh2_streams.SSH2Stream; +var SFTPStream = ssh2_streams.SFTPStream; +var consts = ssh2_streams.constants; +var BUGS = consts.BUGS; +var ALGORITHMS = consts.ALGORITHMS; +var EDDSA_SUPPORTED = consts.EDDSA_SUPPORTED; +var parseKey = ssh2_streams.utils.parseKey; + +var HTTPAgents = __webpack_require__(307); +var Channel = __webpack_require__(89); +var agentQuery = __webpack_require__(393); +var SFTPWrapper = __webpack_require__(33); +var readUInt32BE = __webpack_require__(58).readUInt32BE; + +var MAX_CHANNEL = Math.pow(2, 32) - 1; +var RE_OPENSSH = /^OpenSSH_(?:(?![0-4])\d)|(?:\d{2,})/; +var DEBUG_NOOP = function(msg) {}; + +function Client() { + if (!(this instanceof Client)) + return new Client(); + + EventEmitter.call(this); + + this.config = { + host: undefined, + port: undefined, + localAddress: undefined, + localPort: undefined, + forceIPv4: undefined, + forceIPv6: undefined, + keepaliveCountMax: undefined, + keepaliveInterval: undefined, + readyTimeout: undefined, + + username: undefined, + password: undefined, + privateKey: undefined, + tryKeyboard: undefined, + agent: undefined, + allowAgentFwd: undefined, + authHandler: undefined, + + hostHashAlgo: undefined, + hostHashCb: undefined, + strictVendor: undefined, + debug: undefined + }; + + this._readyTimeout = undefined; + this._channels = undefined; + this._callbacks = undefined; + this._forwarding = undefined; + this._forwardingUnix = undefined; + this._acceptX11 = undefined; + this._agentFwdEnabled = undefined; + this._curChan = undefined; + this._remoteVer = undefined; + + this._sshstream = undefined; + this._sock = undefined; + this._resetKA = undefined; +} +inherits(Client, EventEmitter); + +Client.prototype.connect = function(cfg) { + var self = this; + + if (this._sock && this._sock.writable) { + this.once('close', function() { + self.connect(cfg); + }); + this.end(); + return; + } + + this.config.host = cfg.hostname || cfg.host || 'localhost'; + this.config.port = cfg.port || 22; + this.config.localAddress = (typeof cfg.localAddress === 'string' + ? cfg.localAddress + : undefined); + this.config.localPort = (typeof cfg.localPort === 'string' + || typeof cfg.localPort === 'number' + ? cfg.localPort + : undefined); + this.config.forceIPv4 = cfg.forceIPv4 || false; + this.config.forceIPv6 = cfg.forceIPv6 || false; + this.config.keepaliveCountMax = (typeof cfg.keepaliveCountMax === 'number' + && cfg.keepaliveCountMax >= 0 + ? cfg.keepaliveCountMax + : 3); + this.config.keepaliveInterval = (typeof cfg.keepaliveInterval === 'number' + && cfg.keepaliveInterval > 0 + ? cfg.keepaliveInterval + : 0); + this.config.readyTimeout = (typeof cfg.readyTimeout === 'number' + && cfg.readyTimeout >= 0 + ? cfg.readyTimeout + : 20000); + + var algorithms = { + kex: undefined, + kexBuf: undefined, + cipher: undefined, + cipherBuf: undefined, + serverHostKey: undefined, + serverHostKeyBuf: undefined, + hmac: undefined, + hmacBuf: undefined, + compress: undefined, + compressBuf: undefined + }; + var i; + if (typeof cfg.algorithms === 'object' && cfg.algorithms !== null) { + var algosSupported; + var algoList; + + algoList = cfg.algorithms.kex; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_KEX; + for (i = 0; i < algoList.length; ++i) { + if (algosSupported.indexOf(algoList[i]) === -1) + throw new Error('Unsupported key exchange algorithm: ' + algoList[i]); + } + algorithms.kex = algoList; + } + + algoList = cfg.algorithms.cipher; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_CIPHER; + for (i = 0; i < algoList.length; ++i) { + if (algosSupported.indexOf(algoList[i]) === -1) + throw new Error('Unsupported cipher algorithm: ' + algoList[i]); + } + algorithms.cipher = algoList; + } + + algoList = cfg.algorithms.serverHostKey; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_SERVER_HOST_KEY; + for (i = 0; i < algoList.length; ++i) { + if (algosSupported.indexOf(algoList[i]) === -1) { + throw new Error('Unsupported server host key algorithm: ' + + algoList[i]); + } + } + algorithms.serverHostKey = algoList; + } + + algoList = cfg.algorithms.hmac; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_HMAC; + for (i = 0; i < algoList.length; ++i) { + if (algosSupported.indexOf(algoList[i]) === -1) + throw new Error('Unsupported HMAC algorithm: ' + algoList[i]); + } + algorithms.hmac = algoList; + } + + algoList = cfg.algorithms.compress; + if (Array.isArray(algoList) && algoList.length > 0) { + algosSupported = ALGORITHMS.SUPPORTED_COMPRESS; + for (i = 0; i < algoList.length; ++i) { + if (algosSupported.indexOf(algoList[i]) === -1) + throw new Error('Unsupported compression algorithm: ' + algoList[i]); + } + algorithms.compress = algoList; + } + } + if (algorithms.compress === undefined) { + if (cfg.compress) { + algorithms.compress = ['zlib@openssh.com', 'zlib']; + if (cfg.compress !== 'force') + algorithms.compress.push('none'); + } else if (cfg.compress === false) + algorithms.compress = ['none']; + } + + if (typeof cfg.username === 'string') + this.config.username = cfg.username; + else if (typeof cfg.user === 'string') + this.config.username = cfg.user; + else + throw new Error('Invalid username'); + + this.config.password = (typeof cfg.password === 'string' + ? cfg.password + : undefined); + this.config.privateKey = (typeof cfg.privateKey === 'string' + || Buffer.isBuffer(cfg.privateKey) + ? cfg.privateKey + : undefined); + this.config.localHostname = (typeof cfg.localHostname === 'string' + && cfg.localHostname.length + ? cfg.localHostname + : undefined); + this.config.localUsername = (typeof cfg.localUsername === 'string' + && cfg.localUsername.length + ? cfg.localUsername + : undefined); + this.config.tryKeyboard = (cfg.tryKeyboard === true); + this.config.agent = (typeof cfg.agent === 'string' && cfg.agent.length + ? cfg.agent + : undefined); + this.config.allowAgentFwd = (cfg.agentForward === true + && this.config.agent !== undefined); + var authHandler = this.config.authHandler = ( + typeof cfg.authHandler === 'function' ? cfg.authHandler : undefined + ); + + this.config.strictVendor = (typeof cfg.strictVendor === 'boolean' + ? cfg.strictVendor + : true); + + var debug = this.config.debug = (typeof cfg.debug === 'function' + ? cfg.debug + : DEBUG_NOOP); + + if (cfg.agentForward === true && !this.config.allowAgentFwd) + throw new Error('You must set a valid agent path to allow agent forwarding'); + + var callbacks = this._callbacks = []; + this._channels = {}; + this._forwarding = {}; + this._forwardingUnix = {}; + this._acceptX11 = 0; + this._agentFwdEnabled = false; + this._curChan = -1; + this._remoteVer = undefined; + var privateKey; + + if (this.config.privateKey) { + privateKey = parseKey(this.config.privateKey, cfg.passphrase); + if (privateKey instanceof Error) + throw new Error('Cannot parse privateKey: ' + privateKey.message); + if (Array.isArray(privateKey)) + privateKey = privateKey[0]; // OpenSSH's newer format only stores 1 key for now + if (privateKey.getPrivatePEM() === null) + throw new Error('privateKey value does not contain a (valid) private key'); + } + + var stream = this._sshstream = new SSH2Stream({ + algorithms: algorithms, + debug: (debug === DEBUG_NOOP ? undefined : debug) + }); + var sock = this._sock = (cfg.sock || new Socket()); + + // drain stderr if we are connection hopping using an exec stream + if (this._sock.stderr && typeof this._sock.stderr.resume === 'function') + this._sock.stderr.resume(); + + // keepalive-related + var kainterval = this.config.keepaliveInterval; + var kacountmax = this.config.keepaliveCountMax; + var kacount = 0; + var katimer; + function sendKA() { + if (++kacount > kacountmax) { + clearInterval(katimer); + if (sock.readable) { + var err = new Error('Keepalive timeout'); + err.level = 'client-timeout'; + self.emit('error', err); + sock.destroy(); + } + return; + } + if (sock.writable) { + // append dummy callback to keep correct callback order + callbacks.push(resetKA); + stream.ping(); + } else + clearInterval(katimer); + } + function resetKA() { + if (kainterval > 0) { + kacount = 0; + clearInterval(katimer); + if (sock.writable) + katimer = setInterval(sendKA, kainterval); + } + } + this._resetKA = resetKA; + + stream.on('USERAUTH_BANNER', function(msg) { + self.emit('banner', msg); + }); + + sock.on('connect', function() { + debug('DEBUG: Client: Connected'); + self.emit('connect'); + if (!cfg.sock) + stream.pipe(sock).pipe(stream); + }).on('timeout', function() { + self.emit('timeout'); + }).on('error', function(err) { + clearTimeout(self._readyTimeout); + err.level = 'client-socket'; + self.emit('error', err); + }).on('end', function() { + stream.unpipe(sock); + clearTimeout(self._readyTimeout); + clearInterval(katimer); + self.emit('end'); + }).on('close', function() { + stream.unpipe(sock); + clearTimeout(self._readyTimeout); + clearInterval(katimer); + self.emit('close'); + + // notify outstanding channel requests of disconnection ... + var callbacks_ = callbacks; + var err = new Error('No response from server'); + callbacks = self._callbacks = []; + for (i = 0; i < callbacks_.length; ++i) + callbacks_[i](err); + + // simulate error for any channels waiting to be opened. this is safe + // against successfully opened channels because the success and failure + // event handlers are automatically removed when a success/failure response + // is received + var channels = self._channels; + var chanNos = Object.keys(channels); + self._channels = {}; + for (i = 0; i < chanNos.length; ++i) { + var ev1 = stream.emit('CHANNEL_OPEN_FAILURE:' + chanNos[i], err); + // emitting CHANNEL_CLOSE should be safe too and should help for any + // special channels which might otherwise keep the process alive, such + // as agent forwarding channels which have open unix sockets ... + var ev2 = stream.emit('CHANNEL_CLOSE:' + chanNos[i]); + var earlyCb; + if (!ev1 && !ev2 && (earlyCb = channels[chanNos[i]]) + && typeof earlyCb === 'function') { + earlyCb(err); + } + } + }); + stream.on('drain', function() { + self.emit('drain'); + }).once('header', function(header) { + self._remoteVer = header.versions.software; + if (header.greeting) + self.emit('greeting', header.greeting); + }).on('continue', function() { + self.emit('continue'); + }).on('error', function(err) { + if (err.level === undefined) + err.level = 'protocol'; + else if (err.level === 'handshake') + clearTimeout(self._readyTimeout); + self.emit('error', err); + }).on('end', function() { + sock.resume(); + }); + + if (typeof cfg.hostVerifier === 'function') { + if (HASHES.indexOf(cfg.hostHash) === -1) + throw new Error('Invalid host hash algorithm: ' + cfg.hostHash); + var hashCb = cfg.hostVerifier; + var hasher = crypto.createHash(cfg.hostHash); + stream.once('fingerprint', function(key, verify) { + hasher.update(key); + var ret = hashCb(hasher.digest('hex'), verify); + if (ret !== undefined) + verify(ret); + }); + } + + // begin authentication handling ============================================= + var curAuth; + var curPartial = null; + var curAuthsLeft = null; + var agentKeys; + var agentKeyPos = 0; + var authsAllowed = ['none']; + if (this.config.password !== undefined) + authsAllowed.push('password'); + if (privateKey !== undefined) + authsAllowed.push('publickey'); + if (this.config.agent !== undefined) + authsAllowed.push('agent'); + if (this.config.tryKeyboard) + authsAllowed.push('keyboard-interactive'); + if (privateKey !== undefined + && this.config.localHostname !== undefined + && this.config.localUsername !== undefined) { + authsAllowed.push('hostbased'); + } + + if (authHandler === undefined) { + var authPos = 0; + authHandler = function authHandler(authsLeft, partial, cb) { + if (authPos === authsAllowed.length) + return false; + return authsAllowed[authPos++]; + }; + } + + var hasSentAuth = false; + function doNextAuth(authName) { + hasSentAuth = true; + if (authName === false) { + stream.removeListener('USERAUTH_FAILURE', onUSERAUTH_FAILURE); + stream.removeListener('USERAUTH_PK_OK', onUSERAUTH_PK_OK); + var err = new Error('All configured authentication methods failed'); + err.level = 'client-authentication'; + self.emit('error', err); + if (stream.writable) + self.end(); + return; + } + if (authsAllowed.indexOf(authName) === -1) + throw new Error('Authentication method not allowed: ' + authName); + curAuth = authName; + switch (curAuth) { + case 'password': + stream.authPassword(self.config.username, self.config.password); + break; + case 'publickey': + stream.authPK(self.config.username, privateKey); + stream.once('USERAUTH_PK_OK', onUSERAUTH_PK_OK); + break; + case 'hostbased': + function hostbasedCb(buf, cb) { + var signature = privateKey.sign(buf); + if (signature instanceof Error) { + signature.message = 'Error while signing data with privateKey: ' + + signature.message; + signature.level = 'client-authentication'; + self.emit('error', signature); + return tryNextAuth(); + } + + cb(signature); + } + stream.authHostbased(self.config.username, + privateKey, + self.config.localHostname, + self.config.localUsername, + hostbasedCb); + break; + case 'agent': + agentQuery(self.config.agent, function(err, keys) { + if (err) { + err.level = 'agent'; + self.emit('error', err); + agentKeys = undefined; + return tryNextAuth(); + } else if (keys.length === 0) { + debug('DEBUG: Agent: No keys stored in agent'); + agentKeys = undefined; + return tryNextAuth(); + } + + agentKeys = keys; + agentKeyPos = 0; + + stream.authPK(self.config.username, keys[0]); + stream.once('USERAUTH_PK_OK', onUSERAUTH_PK_OK); + }); + break; + case 'keyboard-interactive': + stream.authKeyboard(self.config.username); + stream.on('USERAUTH_INFO_REQUEST', onUSERAUTH_INFO_REQUEST); + break; + case 'none': + stream.authNone(self.config.username); + break; + } + } + function tryNextAuth() { + hasSentAuth = false; + var auth = authHandler(curAuthsLeft, curPartial, doNextAuth); + if (hasSentAuth || auth === undefined) + return; + doNextAuth(auth); + } + function tryNextAgentKey() { + if (curAuth === 'agent') { + if (agentKeyPos >= agentKeys.length) + return; + if (++agentKeyPos >= agentKeys.length) { + debug('DEBUG: Agent: No more keys left to try'); + debug('DEBUG: Client: agent auth failed'); + agentKeys = undefined; + tryNextAuth(); + } else { + debug('DEBUG: Agent: Trying key #' + (agentKeyPos + 1)); + stream.authPK(self.config.username, agentKeys[agentKeyPos]); + stream.once('USERAUTH_PK_OK', onUSERAUTH_PK_OK); + } + } + } + function onUSERAUTH_INFO_REQUEST(name, instructions, lang, prompts) { + var nprompts = (Array.isArray(prompts) ? prompts.length : 0); + if (nprompts === 0) { + debug('DEBUG: Client: Sending automatic USERAUTH_INFO_RESPONSE'); + return stream.authInfoRes(); + } + // we sent a keyboard-interactive user authentication request and now the + // server is sending us the prompts we need to present to the user + self.emit('keyboard-interactive', + name, + instructions, + lang, + prompts, + function(answers) { + stream.authInfoRes(answers); + } + ); + } + function onUSERAUTH_PK_OK() { + if (curAuth === 'agent') { + var agentKey = agentKeys[agentKeyPos]; + var keyLen = readUInt32BE(agentKey, 0); + var pubKeyFullType = agentKey.toString('ascii', 4, 4 + keyLen); + var pubKeyType = pubKeyFullType.slice(4); + // Check that we support the key type first + // TODO: move key type checking logic to ssh2-streams + switch (pubKeyFullType) { + case 'ssh-rsa': + case 'ssh-dss': + case 'ecdsa-sha2-nistp256': + case 'ecdsa-sha2-nistp384': + case 'ecdsa-sha2-nistp521': + break; + default: + if (EDDSA_SUPPORTED && pubKeyFullType === 'ssh-ed25519') + break; + debug('DEBUG: Agent: Skipping unsupported key type: ' + + pubKeyFullType); + return tryNextAgentKey(); + } + stream.authPK(self.config.username, + agentKey, + function(buf, cb) { + agentQuery(self.config.agent, + agentKey, + pubKeyType, + buf, + function(err, signed) { + if (err) { + err.level = 'agent'; + self.emit('error', err); + } else { + var sigFullTypeLen = readUInt32BE(signed, 0); + if (4 + sigFullTypeLen + 4 < signed.length) { + var sigFullType = signed.toString('ascii', 4, 4 + sigFullTypeLen); + if (sigFullType !== pubKeyFullType) { + err = new Error('Agent key/signature type mismatch'); + err.level = 'agent'; + self.emit('error', err); + } else { + // skip algoLen + algo + sigLen + return cb(signed.slice(4 + sigFullTypeLen + 4)); + } + } + } + + tryNextAgentKey(); + }); + }); + } else if (curAuth === 'publickey') { + stream.authPK(self.config.username, privateKey, function(buf, cb) { + var signature = privateKey.sign(buf); + if (signature instanceof Error) { + signature.message = 'Error while signing data with privateKey: ' + + signature.message; + signature.level = 'client-authentication'; + self.emit('error', signature); + return tryNextAuth(); + } + cb(signature); + }); + } + } + function onUSERAUTH_FAILURE(authsLeft, partial) { + stream.removeListener('USERAUTH_PK_OK', onUSERAUTH_PK_OK); + stream.removeListener('USERAUTH_INFO_REQUEST', onUSERAUTH_INFO_REQUEST); + if (curAuth === 'agent') { + debug('DEBUG: Client: Agent key #' + (agentKeyPos + 1) + ' failed'); + return tryNextAgentKey(); + } else { + debug('DEBUG: Client: ' + curAuth + ' auth failed'); + } + + curPartial = partial; + curAuthsLeft = authsLeft; + tryNextAuth(); + } + stream.once('USERAUTH_SUCCESS', function() { + stream.removeListener('USERAUTH_FAILURE', onUSERAUTH_FAILURE); + stream.removeListener('USERAUTH_INFO_REQUEST', onUSERAUTH_INFO_REQUEST); + + // start keepalive mechanism + resetKA(); + + clearTimeout(self._readyTimeout); + + self.emit('ready'); + }).on('USERAUTH_FAILURE', onUSERAUTH_FAILURE); + // end authentication handling =============================================== + + // handle initial handshake completion + stream.once('ready', function() { + stream.service('ssh-userauth'); + stream.once('SERVICE_ACCEPT', function(svcName) { + if (svcName === 'ssh-userauth') + tryNextAuth(); + }); + }); + + // handle incoming requests from server, typically a forwarded TCP or X11 + // connection + stream.on('CHANNEL_OPEN', function(info) { + onCHANNEL_OPEN(self, info); + }); + + // handle responses for tcpip-forward and other global requests + stream.on('REQUEST_SUCCESS', function(data) { + if (callbacks.length) + callbacks.shift()(false, data); + }).on('REQUEST_FAILURE', function() { + if (callbacks.length) + callbacks.shift()(true); + }); + + stream.on('GLOBAL_REQUEST', function(name, wantReply, data) { + // auto-reject all global requests, this can be especially useful if the + // server is sending us dummy keepalive global requests + if (wantReply) + stream.requestFailure(); + }); + + if (!cfg.sock) { + var host = this.config.host; + var forceIPv4 = this.config.forceIPv4; + var forceIPv6 = this.config.forceIPv6; + + debug('DEBUG: Client: Trying ' + + host + + ' on port ' + + this.config.port + + ' ...'); + + function doConnect() { + startTimeout(); + self._sock.connect({ + host: host, + port: self.config.port, + localAddress: self.config.localAddress, + localPort: self.config.localPort + }); + self._sock.setNoDelay(true); + self._sock.setMaxListeners(0); + self._sock.setTimeout(typeof cfg.timeout === 'number' ? cfg.timeout : 0); + } + + if ((!forceIPv4 && !forceIPv6) || (forceIPv4 && forceIPv6)) + doConnect(); + else { + dnsLookup(host, (forceIPv4 ? 4 : 6), function(err, address, family) { + if (err) { + var error = new Error('Error while looking up ' + + (forceIPv4 ? 'IPv4' : 'IPv6') + + ' address for host ' + + host + + ': ' + err); + clearTimeout(self._readyTimeout); + error.level = 'client-dns'; + self.emit('error', error); + self.emit('close'); + return; + } + host = address; + doConnect(); + }); + } + } else { + startTimeout(); + stream.pipe(sock).pipe(stream); + } + + function startTimeout() { + if (self.config.readyTimeout > 0) { + self._readyTimeout = setTimeout(function() { + var err = new Error('Timed out while waiting for handshake'); + err.level = 'client-timeout'; + self.emit('error', err); + sock.destroy(); + }, self.config.readyTimeout); + } + } +}; + +Client.prototype.end = function() { + if (this._sock + && this._sock.writable + && this._sshstream + && this._sshstream.writable) + return this._sshstream.disconnect(); + return false; +}; + +Client.prototype.destroy = function() { + this._sock && this._sock.destroy(); +}; + +Client.prototype.exec = function(cmd, opts, cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + if (typeof opts === 'function') { + cb = opts; + opts = {}; + } + + var self = this; + var extraOpts = { allowHalfOpen: (opts.allowHalfOpen !== false) }; + + return openChannel(this, 'session', extraOpts, function(err, chan) { + if (err) + return cb(err); + + var todo = []; + + function reqCb(err) { + if (err) { + chan.close(); + return cb(err); + } + if (todo.length) + todo.shift()(); + } + + if (self.config.allowAgentFwd === true + || (opts + && opts.agentForward === true + && self.config.agent !== undefined)) { + todo.push(function() { + reqAgentFwd(chan, reqCb); + }); + } + + if (typeof opts === 'object' && opts !== null) { + if (typeof opts.env === 'object' && opts.env !== null) + reqEnv(chan, opts.env); + if ((typeof opts.pty === 'object' && opts.pty !== null) + || opts.pty === true) { + todo.push(function() { reqPty(chan, opts.pty, reqCb); }); + } + if ((typeof opts.x11 === 'object' && opts.x11 !== null) + || opts.x11 === 'number' + || opts.x11 === true) { + todo.push(function() { reqX11(chan, opts.x11, reqCb); }); + } + } + + todo.push(function() { reqExec(chan, cmd, opts, cb); }); + todo.shift()(); + }); +}; + +Client.prototype.shell = function(wndopts, opts, cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + // start an interactive terminal/shell session + var self = this; + + if (typeof wndopts === 'function') { + cb = wndopts; + wndopts = opts = undefined; + } else if (typeof opts === 'function') { + cb = opts; + opts = undefined; + } + if (wndopts && (wndopts.x11 !== undefined || wndopts.env !== undefined)) { + opts = wndopts; + wndopts = undefined; + } + + return openChannel(this, 'session', function(err, chan) { + if (err) + return cb(err); + + var todo = []; + + function reqCb(err) { + if (err) { + chan.close(); + return cb(err); + } + if (todo.length) + todo.shift()(); + } + + if (self.config.allowAgentFwd === true + || (opts + && opts.agentForward === true + && self.config.agent !== undefined)) { + todo.push(function() { reqAgentFwd(chan, reqCb); }); + } + + if (wndopts !== false) + todo.push(function() { reqPty(chan, wndopts, reqCb); }); + + if (typeof opts === 'object' && opts !== null) { + if (typeof opts.env === 'object' && opts.env !== null) + reqEnv(chan, opts.env); + if ((typeof opts.x11 === 'object' && opts.x11 !== null) + || opts.x11 === 'number' + || opts.x11 === true) { + todo.push(function() { reqX11(chan, opts.x11, reqCb); }); + } + } + + todo.push(function() { reqShell(chan, cb); }); + todo.shift()(); + }); +}; + +Client.prototype.subsys = function(name, cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + return openChannel(this, 'session', function(err, chan) { + if (err) + return cb(err); + + reqSubsystem(chan, name, function(err, stream) { + if (err) + return cb(err); + + cb(undefined, stream); + }); + }); +}; + +Client.prototype.sftp = function(cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + var self = this; + + // start an SFTP session + return openChannel(this, 'session', function(err, chan) { + if (err) + return cb(err); + + reqSubsystem(chan, 'sftp', function(err, stream) { + if (err) + return cb(err); + + var serverIdentRaw = self._sshstream._state.incoming.identRaw; + var cfg = { debug: self.config.debug }; + var sftp = new SFTPStream(cfg, serverIdentRaw); + + function onError(err) { + sftp.removeListener('ready', onReady); + stream.removeListener('exit', onExit); + cb(err); + } + + function onReady() { + sftp.removeListener('error', onError); + stream.removeListener('exit', onExit); + cb(undefined, new SFTPWrapper(sftp)); + } + + function onExit(code, signal) { + sftp.removeListener('ready', onReady); + sftp.removeListener('error', onError); + var msg; + if (typeof code === 'number') { + msg = 'Received exit code ' + + code + + ' while establishing SFTP session'; + } else { + msg = 'Received signal ' + + signal + + ' while establishing SFTP session'; + } + var err = new Error(msg); + err.code = code; + err.signal = signal; + cb(err); + } + + sftp.once('error', onError) + .once('ready', onReady) + .once('close', function() { + stream.end(); + }); + + // OpenSSH server sends an exit-status if there was a problem spinning up + // an sftp server child process, so we listen for that here in order to + // properly raise an error. + stream.once('exit', onExit); + + sftp.pipe(stream).pipe(sftp); + }); + }); +}; + +Client.prototype.forwardIn = function(bindAddr, bindPort, cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + // send a request for the server to start forwarding TCP connections to us + // on a particular address and port + + var self = this; + var wantReply = (typeof cb === 'function'); + + if (wantReply) { + this._callbacks.push(function(had_err, data) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to bind to ' + bindAddr + ':' + bindPort)); + } + + var realPort = bindPort; + if (bindPort === 0 && data && data.length >= 4) { + realPort = readUInt32BE(data, 0); + if (!(self._sshstream.remoteBugs & BUGS.DYN_RPORT_BUG)) + bindPort = realPort; + } + + self._forwarding[bindAddr + ':' + bindPort] = realPort; + + cb(undefined, realPort); + }); + } + + return this._sshstream.tcpipForward(bindAddr, bindPort, wantReply); +}; + +Client.prototype.unforwardIn = function(bindAddr, bindPort, cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + // send a request to stop forwarding us new connections for a particular + // address and port + + var self = this; + var wantReply = (typeof cb === 'function'); + + if (wantReply) { + this._callbacks.push(function(had_err) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to unbind from ' + + bindAddr + ':' + bindPort)); + } + + delete self._forwarding[bindAddr + ':' + bindPort]; + + cb(); + }); + } + + return this._sshstream.cancelTcpipForward(bindAddr, bindPort, wantReply); +}; + +Client.prototype.forwardOut = function(srcIP, srcPort, dstIP, dstPort, cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + // send a request to forward a TCP connection to the server + + var cfg = { + srcIP: srcIP, + srcPort: srcPort, + dstIP: dstIP, + dstPort: dstPort + }; + + return openChannel(this, 'direct-tcpip', cfg, cb); +}; + +Client.prototype.openssh_noMoreSessions = function(cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + var wantReply = (typeof cb === 'function'); + + if (!this.config.strictVendor + || (this.config.strictVendor && RE_OPENSSH.test(this._remoteVer))) { + if (wantReply) { + this._callbacks.push(function(had_err) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to disable future sessions')); + } + + cb(); + }); + } + + return this._sshstream.openssh_noMoreSessions(wantReply); + } else if (wantReply) { + process.nextTick(function() { + cb(new Error('strictVendor enabled and server is not OpenSSH or compatible version')); + }); + } + + return true; +}; + +Client.prototype.openssh_forwardInStreamLocal = function(socketPath, cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + var wantReply = (typeof cb === 'function'); + var self = this; + + if (!this.config.strictVendor + || (this.config.strictVendor && RE_OPENSSH.test(this._remoteVer))) { + if (wantReply) { + this._callbacks.push(function(had_err) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to bind to ' + socketPath)); + } + self._forwardingUnix[socketPath] = true; + cb(); + }); + } + + return this._sshstream.openssh_streamLocalForward(socketPath, wantReply); + } else if (wantReply) { + process.nextTick(function() { + cb(new Error('strictVendor enabled and server is not OpenSSH or compatible version')); + }); + } + + return true; +}; + +Client.prototype.openssh_unforwardInStreamLocal = function(socketPath, cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + var wantReply = (typeof cb === 'function'); + var self = this; + + if (!this.config.strictVendor + || (this.config.strictVendor && RE_OPENSSH.test(this._remoteVer))) { + if (wantReply) { + this._callbacks.push(function(had_err) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to unbind on ' + socketPath)); + } + delete self._forwardingUnix[socketPath]; + cb(); + }); + } + + return this._sshstream.openssh_cancelStreamLocalForward(socketPath, + wantReply); + } else if (wantReply) { + process.nextTick(function() { + cb(new Error('strictVendor enabled and server is not OpenSSH or compatible version')); + }); + } + + return true; +}; + +Client.prototype.openssh_forwardOutStreamLocal = function(socketPath, cb) { + if (!this._sock + || !this._sock.writable + || !this._sshstream + || !this._sshstream.writable) + throw new Error('Not connected'); + + if (!this.config.strictVendor + || (this.config.strictVendor && RE_OPENSSH.test(this._remoteVer))) { + var cfg = { socketPath: socketPath }; + return openChannel(this, 'direct-streamlocal@openssh.com', cfg, cb); + } else { + process.nextTick(function() { + cb(new Error('strictVendor enabled and server is not OpenSSH or compatible version')); + }); + } + + return true; +}; + +function openChannel(self, type, opts, cb) { + // ask the server to open a channel for some purpose + // (e.g. session (sftp, exec, shell), or forwarding a TCP connection + var localChan = nextChannel(self); + var initWindow = Channel.MAX_WINDOW; + var maxPacket = Channel.PACKET_SIZE; + var ret = true; + + if (localChan === false) + return cb(new Error('No free channels available')); + + if (typeof opts === 'function') { + cb = opts; + opts = {}; + } + + self._channels[localChan] = cb; + + var sshstream = self._sshstream; + sshstream.once('CHANNEL_OPEN_CONFIRMATION:' + localChan, onSuccess) + .once('CHANNEL_OPEN_FAILURE:' + localChan, onFailure) + .once('CHANNEL_CLOSE:' + localChan, onFailure); + + if (type === 'session') + ret = sshstream.session(localChan, initWindow, maxPacket); + else if (type === 'direct-tcpip') + ret = sshstream.directTcpip(localChan, initWindow, maxPacket, opts); + else if (type === 'direct-streamlocal@openssh.com') { + ret = sshstream.openssh_directStreamLocal(localChan, + initWindow, + maxPacket, + opts); + } + + return ret; + + function onSuccess(info) { + sshstream.removeListener('CHANNEL_OPEN_FAILURE:' + localChan, onFailure); + sshstream.removeListener('CHANNEL_CLOSE:' + localChan, onFailure); + + var chaninfo = { + type: type, + incoming: { + id: localChan, + window: initWindow, + packetSize: maxPacket, + state: 'open' + }, + outgoing: { + id: info.sender, + window: info.window, + packetSize: info.packetSize, + state: 'open' + } + }; + cb(undefined, new Channel(chaninfo, self)); + } + + function onFailure(info) { + sshstream.removeListener('CHANNEL_OPEN_CONFIRMATION:' + localChan, + onSuccess); + sshstream.removeListener('CHANNEL_OPEN_FAILURE:' + localChan, onFailure); + sshstream.removeListener('CHANNEL_CLOSE:' + localChan, onFailure); + + delete self._channels[localChan]; + + var err; + if (info instanceof Error) + err = info; + else if (typeof info === 'object' && info !== null) { + err = new Error('(SSH) Channel open failure: ' + info.description); + err.reason = info.reason; + err.lang = info.lang; + } else { + err = new Error('(SSH) Channel open failure: ' + + 'server closed channel unexpectedly'); + err.reason = err.lang = ''; + } + cb(err); + } +} + +function nextChannel(self) { + // get the next available channel number + + // optimized path + if (self._curChan < MAX_CHANNEL) + return ++self._curChan; + + // slower lookup path + for (var i = 0, channels = self._channels; i < MAX_CHANNEL; ++i) + if (!channels[i]) + return i; + + return false; +} + +function reqX11(chan, screen, cb) { + // asks server to start sending us X11 connections + var cfg = { + single: false, + protocol: 'MIT-MAGIC-COOKIE-1', + cookie: undefined, + screen: 0 + }; + + if (typeof screen === 'function') { + cb = screen; + } else if (typeof screen === 'object' && screen !== null) { + if (typeof screen.single === 'boolean') + cfg.single = screen.single; + if (typeof screen.screen === 'number') + cfg.screen = screen.screen; + if (typeof screen.protocol === 'string') + cfg.protocol = screen.protocol; + if (typeof screen.cookie === 'string') + cfg.cookie = screen.cookie; + else if (Buffer.isBuffer(screen.cookie)) + cfg.cookie = screen.cookie.toString('hex'); + } + if (cfg.cookie === undefined) + cfg.cookie = randomCookie(); + + var wantReply = (typeof cb === 'function'); + + if (chan.outgoing.state !== 'open') { + wantReply && cb(new Error('Channel is not open')); + return true; + } + + if (wantReply) { + chan._callbacks.push(function(had_err) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to request X11')); + } + + chan._hasX11 = true; + ++chan._client._acceptX11; + chan.once('close', function() { + if (chan._client._acceptX11) + --chan._client._acceptX11; + }); + + cb(); + }); + } + + return chan._client._sshstream.x11Forward(chan.outgoing.id, cfg, wantReply); +} + +function reqPty(chan, opts, cb) { + var rows = 24; + var cols = 80; + var width = 640; + var height = 480; + var term = 'vt100'; + var modes = null; + + if (typeof opts === 'function') + cb = opts; + else if (typeof opts === 'object' && opts !== null) { + if (typeof opts.rows === 'number') + rows = opts.rows; + if (typeof opts.cols === 'number') + cols = opts.cols; + if (typeof opts.width === 'number') + width = opts.width; + if (typeof opts.height === 'number') + height = opts.height; + if (typeof opts.term === 'string') + term = opts.term; + if (typeof opts.modes === 'object') + modes = opts.modes; + } + + var wantReply = (typeof cb === 'function'); + + if (chan.outgoing.state !== 'open') { + wantReply && cb(new Error('Channel is not open')); + return true; + } + + if (wantReply) { + chan._callbacks.push(function(had_err) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to request a pseudo-terminal')); + } + cb(); + }); + } + + return chan._client._sshstream.pty(chan.outgoing.id, + rows, + cols, + height, + width, + term, + modes, + wantReply); +} + +function reqAgentFwd(chan, cb) { + var wantReply = (typeof cb === 'function'); + + if (chan.outgoing.state !== 'open') { + wantReply && cb(new Error('Channel is not open')); + return true; + } else if (chan._client._agentFwdEnabled) { + wantReply && cb(false); + return true; + } + + chan._client._agentFwdEnabled = true; + + chan._callbacks.push(function(had_err) { + if (had_err) { + chan._client._agentFwdEnabled = false; + wantReply && cb(had_err !== true + ? had_err + : new Error('Unable to request agent forwarding')); + return; + } + + wantReply && cb(); + }); + + return chan._client._sshstream.openssh_agentForward(chan.outgoing.id, true); +} + +function reqShell(chan, cb) { + if (chan.outgoing.state !== 'open') { + cb(new Error('Channel is not open')); + return true; + } + chan._callbacks.push(function(had_err) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to open shell')); + } + chan.subtype = 'shell'; + cb(undefined, chan); + }); + + return chan._client._sshstream.shell(chan.outgoing.id, true); +} + +function reqExec(chan, cmd, opts, cb) { + if (chan.outgoing.state !== 'open') { + cb(new Error('Channel is not open')); + return true; + } + chan._callbacks.push(function(had_err) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to exec')); + } + chan.subtype = 'exec'; + chan.allowHalfOpen = (opts.allowHalfOpen !== false); + cb(undefined, chan); + }); + + return chan._client._sshstream.exec(chan.outgoing.id, cmd, true); +} + +function reqEnv(chan, env) { + if (chan.outgoing.state !== 'open') + return true; + var ret = true; + var keys = Object.keys(env || {}); + var key; + var val; + + for (var i = 0, len = keys.length; i < len; ++i) { + key = keys[i]; + val = env[key]; + ret = chan._client._sshstream.env(chan.outgoing.id, key, val, false); + } + + return ret; +} + +function reqSubsystem(chan, name, cb) { + if (chan.outgoing.state !== 'open') { + cb(new Error('Channel is not open')); + return true; + } + chan._callbacks.push(function(had_err) { + if (had_err) { + return cb(had_err !== true + ? had_err + : new Error('Unable to start subsystem: ' + name)); + } + chan.subtype = 'subsystem'; + cb(undefined, chan); + }); + + return chan._client._sshstream.subsystem(chan.outgoing.id, name, true); +} + +function onCHANNEL_OPEN(self, info) { + // the server is trying to open a channel with us, this is usually when + // we asked the server to forward us connections on some port and now they + // are asking us to accept/deny an incoming connection on their side + + var localChan = false; + var reason; + + function accept() { + var chaninfo = { + type: info.type, + incoming: { + id: localChan, + window: Channel.MAX_WINDOW, + packetSize: Channel.PACKET_SIZE, + state: 'open' + }, + outgoing: { + id: info.sender, + window: info.window, + packetSize: info.packetSize, + state: 'open' + } + }; + var stream = new Channel(chaninfo, self); + + self._sshstream.channelOpenConfirm(info.sender, + localChan, + Channel.MAX_WINDOW, + Channel.PACKET_SIZE); + return stream; + } + function reject() { + if (reason === undefined) { + if (localChan === false) + reason = consts.CHANNEL_OPEN_FAILURE.RESOURCE_SHORTAGE; + else + reason = consts.CHANNEL_OPEN_FAILURE.CONNECT_FAILED; + } + + self._sshstream.channelOpenFail(info.sender, reason, '', ''); + } + + if (info.type === 'forwarded-tcpip' + || info.type === 'x11' + || info.type === 'auth-agent@openssh.com' + || info.type === 'forwarded-streamlocal@openssh.com') { + + // check for conditions for automatic rejection + var rejectConn = ( + (info.type === 'forwarded-tcpip' + && self._forwarding[info.data.destIP + + ':' + + info.data.destPort] === undefined) + || (info.type === 'forwarded-streamlocal@openssh.com' + && self._forwardingUnix[info.data.socketPath] === undefined) + || (info.type === 'x11' && self._acceptX11 === 0) + || (info.type === 'auth-agent@openssh.com' + && !self._agentFwdEnabled) + ); + + if (!rejectConn) { + localChan = nextChannel(self); + + if (localChan === false) { + self.config.debug('DEBUG: Client: Automatic rejection of incoming channel open: no channels available'); + rejectConn = true; + } else + self._channels[localChan] = true; + } else { + reason = consts.CHANNEL_OPEN_FAILURE.ADMINISTRATIVELY_PROHIBITED; + self.config.debug('DEBUG: Client: Automatic rejection of incoming channel open: unexpected channel open for: ' + + info.type); + } + + // TODO: automatic rejection after some timeout? + + if (rejectConn) + reject(); + + if (localChan !== false) { + if (info.type === 'forwarded-tcpip') { + if (info.data.destPort === 0) { + info.data.destPort = self._forwarding[info.data.destIP + + ':' + + info.data.destPort]; + } + self.emit('tcp connection', info.data, accept, reject); + } else if (info.type === 'x11') { + self.emit('x11', info.data, accept, reject); + } else if (info.type === 'forwarded-streamlocal@openssh.com') { + self.emit('unix connection', info.data, accept, reject); + } else { + agentQuery(self.config.agent, accept, reject); + } + } + } else { + // automatically reject any unsupported channel open requests + self.config.debug('DEBUG: Client: Automatic rejection of incoming channel open: unsupported type: ' + + info.type); + reason = consts.CHANNEL_OPEN_FAILURE.UNKNOWN_CHANNEL_TYPE; + reject(); + } +} + +var randomCookie = (function() { + if (typeof crypto.randomFillSync === 'function') { + var buffer = Buffer.alloc(16); + return function randomCookie() { + crypto.randomFillSync(buffer, 0, 16); + return buffer.toString('hex'); + }; + } else { + return function randomCookie() { + return crypto.randomBytes(16).toString('hex'); + }; + } +})(); + +Client.Client = Client; +Client.Server = __webpack_require__(279); +// pass some useful utilities on to end user (e.g. parseKey()) +Client.utils = ssh2_streams.utils; +// expose useful SFTPStream constants for sftp server usage +Client.SFTP_STATUS_CODE = SFTPStream.STATUS_CODE; +Client.SFTP_OPEN_MODE = SFTPStream.OPEN_MODE; +// expose http(s).Agent implementations to allow easy tunneling of HTTP(S) +// requests +Client.HTTPAgent = HTTPAgents.SSHTTPAgent; +Client.HTTPSAgent = HTTPAgents.SSHTTPSAgent; + +module.exports = Client; // backwards compatibility + + +/***/ }), + +/***/ 599: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var inspect = __webpack_require__(669).inspect; + +function assert(value, message) { + if (!value) + throw new ERR_INTERNAL_ASSERTION(message); +} +assert.fail = function fail(message) { + throw new ERR_INTERNAL_ASSERTION(message); +}; + +// Only use this for integers! Decimal numbers do not work with this function. +function addNumericalSeparator(val) { + var res = ''; + var i = val.length; + var start = val[0] === '-' ? 1 : 0; + for (; i >= start + 4; i -= 3) + res = `_${val.slice(i - 3, i)}${res}`; + return `${val.slice(0, i)}${res}`; +} + +function oneOf(expected, thing) { + assert(typeof thing === 'string', '`thing` has to be of type string'); + if (Array.isArray(expected)) { + var len = expected.length; + assert(len > 0, 'At least one expected value needs to be specified'); + expected = expected.map((i) => String(i)); + if (len > 2) { + return `one of ${thing} ${expected.slice(0, len - 1).join(', ')}, or ` + + expected[len - 1]; + } else if (len === 2) { + return `one of ${thing} ${expected[0]} or ${expected[1]}`; + } else { + return `of ${thing} ${expected[0]}`; + } + } else { + return `of ${thing} ${String(expected)}`; + } +} + + + +exports.ERR_INTERNAL_ASSERTION = class ERR_INTERNAL_ASSERTION extends Error { + constructor(message) { + super(); + Error.captureStackTrace(this, ERR_INTERNAL_ASSERTION); + + var suffix = 'This is caused by either a bug in ssh2-streams ' + + 'or incorrect usage of ssh2-streams internals.\n' + + 'Please open an issue with this stack trace at ' + + 'https://github.com/mscdex/ssh2-streams/issues\n'; + + this.message = (message === undefined ? suffix : `${message}\n${suffix}`); + } +}; + +var MAX_32BIT_INT = Math.pow(2, 32); +var MAX_32BIT_BIGINT = (function() { + try { + return new Function('return 2n ** 32n')(); + } catch (ex) {} +})(); +exports.ERR_OUT_OF_RANGE = class ERR_OUT_OF_RANGE extends RangeError { + constructor(str, range, input, replaceDefaultBoolean) { + super(); + Error.captureStackTrace(this, ERR_OUT_OF_RANGE); + + assert(range, 'Missing "range" argument'); + var msg = (replaceDefaultBoolean + ? str + : `The value of "${str}" is out of range.`); + var received; + if (Number.isInteger(input) && Math.abs(input) > MAX_32BIT_INT) { + received = addNumericalSeparator(String(input)); + } else if (typeof input === 'bigint') { + received = String(input); + if (input > MAX_32BIT_BIGINT || input < -MAX_32BIT_BIGINT) + received = addNumericalSeparator(received); + received += 'n'; + } else { + received = inspect(input); + } + msg += ` It must be ${range}. Received ${received}`; + + this.message = msg; + } +}; + +exports.ERR_INVALID_ARG_TYPE = class ERR_INVALID_ARG_TYPE extends TypeError { + constructor(name, expected, actual) { + super(); + Error.captureStackTrace(this, ERR_INVALID_ARG_TYPE); + + assert(typeof name === 'string', `'name' must be a string`); + + // determiner: 'must be' or 'must not be' + var determiner; + if (typeof expected === 'string' && expected.startsWith('not ')) { + determiner = 'must not be'; + expected = expected.replace(/^not /, ''); + } else { + determiner = 'must be'; + } + + var msg; + if (name.endsWith(' argument')) { + // For cases like 'first argument' + msg = `The ${name} ${determiner} ${oneOf(expected, 'type')}`; + } else { + var type = (name.includes('.') ? 'property' : 'argument'); + msg = `The "${name}" ${type} ${determiner} ${oneOf(expected, 'type')}`; + } + + msg += `. Received type ${typeof actual}`; + + this.message = msg; + } +}; + +exports.validateNumber = function validateNumber(value, name) { + if (typeof value !== 'number') + throw new ERR_INVALID_ARG_TYPE(name, 'number', value); +}; + + +// ============================================================================= +// Following code is only needed to support node v6.x .... + +// Undocumented cb() API, needed for core, not for public API +exports.destroyImpl = function destroy(err, cb) { + const readableDestroyed = this._readableState && + this._readableState.destroyed; + const writableDestroyed = this._writableState && + this._writableState.destroyed; + + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err) { + if (!this._writableState) { + process.nextTick(emitErrorNT, this, err); + } else if (!this._writableState.errorEmitted) { + this._writableState.errorEmitted = true; + process.nextTick(emitErrorNT, this, err); + } + } + + return this; + } + + // We set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks + + if (this._readableState) { + this._readableState.destroyed = true; + } + + // If this is a duplex stream mark the writable part as destroyed as well + if (this._writableState) { + this._writableState.destroyed = true; + } + + this._destroy(err || null, (err) => { + if (!cb && err) { + if (!this._writableState) { + process.nextTick(emitErrorAndCloseNT, this, err); + } else if (!this._writableState.errorEmitted) { + this._writableState.errorEmitted = true; + process.nextTick(emitErrorAndCloseNT, this, err); + } else { + process.nextTick(emitCloseNT, this); + } + } else if (cb) { + process.nextTick(emitCloseNT, this); + cb(err); + } else { + process.nextTick(emitCloseNT, this); + } + }); + + return this; +}; + +function emitErrorAndCloseNT(self, err) { + emitErrorNT(self, err); + emitCloseNT(self); +} + +function emitCloseNT(self) { + if (self._writableState && !self._writableState.emitClose) + return; + if (self._readableState && !self._readableState.emitClose) + return; + self.emit('close'); +} +// ============================================================================= + + +/***/ }), + +/***/ 605: +/***/ (function(module) { + +module.exports = require("http"); + +/***/ }), + +/***/ 614: +/***/ (function(module) { + +module.exports = require("events"); + +/***/ }), + +/***/ 619: +/***/ (function(module) { + +module.exports = require("constants"); + +/***/ }), + +/***/ 622: +/***/ (function(module) { + +module.exports = require("path"); + +/***/ }), + +/***/ 627: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents an Task + * @param {Object} modem docker-modem + * @param {String} id Task's ID + */ +var Task = function(modem, id) { + this.modem = modem; + this.id = id; + + this.defaultOptions = { + log: {} + }; +}; + +Task.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Query Docker for Task details. + * + * @param {function} callback + */ +Task.prototype.inspect = function(callback) { + var self = this; + + var optsf = { + path: '/tasks/' + this.id, + method: 'GET', + statusCodes: { + 200: true, + 404: 'unknown task', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Task logs + * @param {Object} opts Logs options. (optional) + * @param {Function} callback Callback with data + */ +Task.prototype.logs = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback, this.defaultOptions.log); + + var optsf = { + path: '/tasks/' + this.id + '/logs?', + method: 'GET', + isStream: args.opts.follow || false, + statusCodes: { + 101: true, + 200: true, + 404: 'no such container', + 500: 'server error', + 503: 'node is not part of a swarm' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + +module.exports = Task; + + +/***/ }), + +/***/ 631: +/***/ (function(module) { + +module.exports = require("net"); + +/***/ }), + +/***/ 635: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// TODO: * Automatic re-key every (configurable) n bytes or length of time +// - RFC suggests every 1GB of transmitted data or 1 hour, whichever +// comes sooner +// * Filter control codes from strings +// (as per http://tools.ietf.org/html/rfc4251#section-9.2) + +var crypto = __webpack_require__(417); +var zlib = __webpack_require__(761); +var TransformStream = __webpack_require__(413).Transform; +var inherits = __webpack_require__(669).inherits; +var inspect = __webpack_require__(669).inspect; + +var StreamSearch = __webpack_require__(458); +var Ber = __webpack_require__(62).Ber; + +var readUInt32BE = __webpack_require__(523).readUInt32BE; +var writeUInt32BE = __webpack_require__(523).writeUInt32BE; +var consts = __webpack_require__(801); +var utils = __webpack_require__(780); +var iv_inc = utils.iv_inc; +var readString = utils.readString; +var readInt = utils.readInt; +var DSASigBERToBare = utils.DSASigBERToBare; +var ECDSASigASN1ToSSH = utils.ECDSASigASN1ToSSH; +var sigSSHToASN1 = utils.sigSSHToASN1; +var parseDERKey = __webpack_require__(798).parseDERKey; + +var CIPHER_INFO = consts.CIPHER_INFO; +var HMAC_INFO = consts.HMAC_INFO; +var MESSAGE = consts.MESSAGE; +var DYNAMIC_KEXDH_MESSAGE = consts.DYNAMIC_KEXDH_MESSAGE; +var KEXDH_MESSAGE = consts.KEXDH_MESSAGE; +var ALGORITHMS = consts.ALGORITHMS; +var DISCONNECT_REASON = consts.DISCONNECT_REASON; +var CHANNEL_OPEN_FAILURE = consts.CHANNEL_OPEN_FAILURE; +var SSH_TO_OPENSSL = consts.SSH_TO_OPENSSL; +var TERMINAL_MODE = consts.TERMINAL_MODE; +var SIGNALS = consts.SIGNALS; +var EDDSA_SUPPORTED = consts.EDDSA_SUPPORTED; +var CURVE25519_SUPPORTED = consts.CURVE25519_SUPPORTED; +var BUGS = consts.BUGS; +var BUGGY_IMPLS = consts.BUGGY_IMPLS; +var BUGGY_IMPLS_LEN = BUGGY_IMPLS.length; +var MODULE_VER = __webpack_require__(724).version; +var I = 0; +var IN_INIT = I++; +var IN_GREETING = I++; +var IN_HEADER = I++; +var IN_PACKETBEFORE = I++; +var IN_PACKET = I++; +var IN_PACKETDATA = I++; +var IN_PACKETDATAVERIFY = I++; +var IN_PACKETDATAAFTER = I++; +var OUT_INIT = I++; +var OUT_READY = I++; +var OUT_REKEYING = I++; +var MAX_SEQNO = 4294967295; +var MAX_PACKET_SIZE = 35000; +var MAX_PACKETS_REKEYING = 50; +var EXP_TYPE_HEADER = 0; +var EXP_TYPE_LF = 1; +var EXP_TYPE_BYTES = 2; // Waits until n bytes have been seen +var Z_PARTIAL_FLUSH = zlib.Z_PARTIAL_FLUSH; +var ZLIB_OPTS = { flush: Z_PARTIAL_FLUSH }; + +var RE_NULL = /\x00/g; + +var IDENT_PREFIX_BUFFER = Buffer.from('SSH-'); +var EMPTY_BUFFER = Buffer.allocUnsafe(0); +var HMAC_COMPUTE = Buffer.allocUnsafe(9); +var PING_PACKET = Buffer.from([ + MESSAGE.GLOBAL_REQUEST, + // "keepalive@openssh.com" + 0, 0, 0, 21, + 107, 101, 101, 112, 97, 108, 105, 118, 101, 64, 111, 112, 101, 110, 115, + 115, 104, 46, 99, 111, 109, + // Request a reply + 1 +]); +var NEWKEYS_PACKET = Buffer.from([MESSAGE.NEWKEYS]); +var USERAUTH_SUCCESS_PACKET = Buffer.from([MESSAGE.USERAUTH_SUCCESS]); +var REQUEST_SUCCESS_PACKET = Buffer.from([MESSAGE.REQUEST_SUCCESS]); +var REQUEST_FAILURE_PACKET = Buffer.from([MESSAGE.REQUEST_FAILURE]); +var NO_TERMINAL_MODES_BUFFER = Buffer.from([TERMINAL_MODE.TTY_OP_END]); +var KEXDH_GEX_REQ_PACKET = Buffer.from([ + MESSAGE.KEXDH_GEX_REQUEST, + // Minimal size in bits of an acceptable group + 0, 0, 4, 0, // 1024, modp2 + // Preferred size in bits of the group the server will send + 0, 0, 16, 0, // 4096, modp16 + // Maximal size in bits of an acceptable group + 0, 0, 32, 0 // 8192, modp18 +]); + +function DEBUG_NOOP(msg) {} + +function SSH2Stream(cfg) { + if (typeof cfg !== 'object' || cfg === null) + cfg = {}; + + TransformStream.call(this, { + highWaterMark: (typeof cfg.highWaterMark === 'number' + ? cfg.highWaterMark + : 32 * 1024) + }); + + this._needContinue = false; + this.bytesSent = this.bytesReceived = 0; + this.debug = (typeof cfg.debug === 'function' ? cfg.debug : DEBUG_NOOP); + this.server = (cfg.server === true); + this.maxPacketSize = (typeof cfg.maxPacketSize === 'number' + ? cfg.maxPacketSize + : MAX_PACKET_SIZE); + // Bitmap that indicates any bugs the remote side has. This is determined + // by the reported software version. + this.remoteBugs = 0; + + if (this.server) { + // TODO: Remove when we support group exchange for server implementation + this.remoteBugs = BUGS.BAD_DHGEX; + } + + this.readable = true; + + var self = this; + + var hostKeys = cfg.hostKeys; + if (this.server && (typeof hostKeys !== 'object' || hostKeys === null)) + throw new Error('hostKeys must be an object keyed on host key type'); + + this.config = { + // Server + hostKeys: hostKeys, // All keys supported by server + + // Client/Server + ident: 'SSH-2.0-' + + (cfg.ident + || ('ssh2js' + MODULE_VER + (this.server ? 'srv' : ''))), + algorithms: { + kex: ALGORITHMS.KEX, + kexBuf: ALGORITHMS.KEX_BUF, + serverHostKey: ALGORITHMS.SERVER_HOST_KEY, + serverHostKeyBuf: ALGORITHMS.SERVER_HOST_KEY_BUF, + cipher: ALGORITHMS.CIPHER, + cipherBuf: ALGORITHMS.CIPHER_BUF, + hmac: ALGORITHMS.HMAC, + hmacBuf: ALGORITHMS.HMAC_BUF, + compress: ALGORITHMS.COMPRESS, + compressBuf: ALGORITHMS.COMPRESS_BUF + } + }; + // RFC 4253 states the identification string must not contain NULL + this.config.ident.replace(RE_NULL, ''); + + if (this.config.ident.length + 2 /* Account for "\r\n" */ > 255) + throw new Error('ident too long'); + + if (typeof cfg.algorithms === 'object' && cfg.algorithms !== null) { + var algos = cfg.algorithms; + if (Array.isArray(algos.kex) && algos.kex.length > 0) { + this.config.algorithms.kex = algos.kex; + if (!Buffer.isBuffer(algos.kexBuf)) + algos.kexBuf = Buffer.from(algos.kex.join(','), 'ascii'); + this.config.algorithms.kexBuf = algos.kexBuf; + } + if (Array.isArray(algos.serverHostKey) && algos.serverHostKey.length > 0) { + this.config.algorithms.serverHostKey = algos.serverHostKey; + if (!Buffer.isBuffer(algos.serverHostKeyBuf)) { + algos.serverHostKeyBuf = Buffer.from(algos.serverHostKey.join(','), + 'ascii'); + } + this.config.algorithms.serverHostKeyBuf = algos.serverHostKeyBuf; + } + if (Array.isArray(algos.cipher) && algos.cipher.length > 0) { + this.config.algorithms.cipher = algos.cipher; + if (!Buffer.isBuffer(algos.cipherBuf)) + algos.cipherBuf = Buffer.from(algos.cipher.join(','), 'ascii'); + this.config.algorithms.cipherBuf = algos.cipherBuf; + } + if (Array.isArray(algos.hmac) && algos.hmac.length > 0) { + this.config.algorithms.hmac = algos.hmac; + if (!Buffer.isBuffer(algos.hmacBuf)) + algos.hmacBuf = Buffer.from(algos.hmac.join(','), 'ascii'); + this.config.algorithms.hmacBuf = algos.hmacBuf; + } + if (Array.isArray(algos.compress) && algos.compress.length > 0) { + this.config.algorithms.compress = algos.compress; + if (!Buffer.isBuffer(algos.compressBuf)) + algos.compressBuf = Buffer.from(algos.compress.join(','), 'ascii'); + this.config.algorithms.compressBuf = algos.compressBuf; + } + } + + this.reset(true); + + // Common events + this.on('end', function() { + // Let GC collect any Buffers we were previously storing + self.readable = false; + self._state = undefined; + self.reset(); + self._state.outgoing.bufSeqno = undefined; + }); + this.on('DISCONNECT', function(reason, code, desc, lang) { + onDISCONNECT(self, reason, code, desc, lang); + }); + this.on('KEXINIT', function(init, firstFollows) { + onKEXINIT(self, init, firstFollows); + }); + this.on('NEWKEYS', function() { onNEWKEYS(self); }); + + if (this.server) { + // Server-specific events + this.on('KEXDH_INIT', function(e) { onKEXDH_INIT(self, e); }); + } else { + // Client-specific events + this.on('KEXDH_REPLY', function(info) { onKEXDH_REPLY(self, info); }) + .on('KEXDH_GEX_GROUP', + function(prime, gen) { onKEXDH_GEX_GROUP(self, prime, gen); }); + } + + if (this.server) { + // Greeting displayed before the ssh identification string is sent, this is + // usually ignored by most clients + if (typeof cfg.greeting === 'string' && cfg.greeting.length) { + if (cfg.greeting.slice(-2) === '\r\n') + this.push(cfg.greeting); + else + this.push(cfg.greeting + '\r\n'); + } + // Banner shown after the handshake completes, but before user + // authentication begins + if (typeof cfg.banner === 'string' && cfg.banner.length) { + if (cfg.banner.slice(-2) === '\r\n') + this.banner = cfg.banner; + else + this.banner = cfg.banner + '\r\n'; + } + } + this.debug('DEBUG: Local ident: ' + inspect(this.config.ident)); + this.push(this.config.ident + '\r\n'); + + this._state.incoming.expectedPacket = 'KEXINIT'; +} +inherits(SSH2Stream, TransformStream); + +SSH2Stream.prototype.__read = TransformStream.prototype._read; +SSH2Stream.prototype._read = function(n) { + if (this._needContinue) { + this._needContinue = false; + this.emit('continue'); + } + return this.__read(n); +}; +SSH2Stream.prototype.__push = TransformStream.prototype.push; +SSH2Stream.prototype.push = function(chunk, encoding) { + var ret = this.__push(chunk, encoding); + this._needContinue = (ret === false); + return ret; +}; + +SSH2Stream.prototype._cleanup = function(callback) { + this.reset(); + this.debug('DEBUG: Parser: Malformed packet'); + callback && callback(new Error('Malformed packet')); +}; + +SSH2Stream.prototype._transform = function(chunk, encoding, callback, decomp) { + var skipDecrypt = false; + var decryptAuthMode = false; + var state = this._state; + var instate = state.incoming; + var outstate = state.outgoing; + var expect = instate.expect; + var decrypt = instate.decrypt; + var decompress = instate.decompress; + var chlen = chunk.length; + var chleft = 0; + var debug = this.debug; + var self = this; + var i = 0; + var p = i; + var blockLen; + var buffer; + var buf; + var r; + + this.bytesReceived += chlen; + + while (true) { + if (expect.type !== undefined) { + if (i >= chlen) + break; + if (expect.type === EXP_TYPE_BYTES) { + chleft = (chlen - i); + var pktLeft = (expect.buf.length - expect.ptr); + if (pktLeft <= chleft) { + chunk.copy(expect.buf, expect.ptr, i, i + pktLeft); + i += pktLeft; + buffer = expect.buf; + expect.buf = undefined; + expect.ptr = 0; + expect.type = undefined; + } else { + chunk.copy(expect.buf, expect.ptr, i); + expect.ptr += chleft; + i += chleft; + } + continue; + } else if (expect.type === EXP_TYPE_HEADER) { + i += instate.search.push(chunk); + if (expect.type !== undefined) + continue; + } else if (expect.type === EXP_TYPE_LF) { + if (++expect.ptr + 4 /* Account for "SSH-" */ > 255) { + this.reset(); + debug('DEBUG: Parser: Identification string exceeded 255 characters'); + return callback(new Error('Max identification string size exceeded')); + } + if (chunk[i] === 0x0A) { + expect.type = undefined; + if (p < i) { + if (expect.buf === undefined) + expect.buf = chunk.toString('ascii', p, i); + else + expect.buf += chunk.toString('ascii', p, i); + } + buffer = expect.buf; + expect.buf = undefined; + ++i; + } else { + if (++i === chlen && p < i) { + if (expect.buf === undefined) + expect.buf = chunk.toString('ascii', p, i); + else + expect.buf += chunk.toString('ascii', p, i); + } + continue; + } + } + } + + if (instate.status === IN_INIT) { + if (!this.readable) + return callback(); + if (this.server) { + // Retrieve what should be the start of the protocol version exchange + if (!buffer) { + debug('DEBUG: Parser: IN_INIT (waiting for identification begin)'); + expectData(this, EXP_TYPE_BYTES, 4); + } else { + if (buffer[0] === 0x53 // S + && buffer[1] === 0x53 // S + && buffer[2] === 0x48 // H + && buffer[3] === 0x2D) { // - + instate.status = IN_GREETING; + debug('DEBUG: Parser: IN_INIT (waiting for rest of identification)'); + } else { + this.reset(); + debug('DEBUG: Parser: Bad identification start'); + return callback(new Error('Bad identification start')); + } + } + } else { + debug('DEBUG: Parser: IN_INIT'); + // Retrieve any bytes that may come before the protocol version exchange + var ss = instate.search = new StreamSearch(IDENT_PREFIX_BUFFER); + ss.on('info', function onInfo(matched, data, start, end) { + if (data) { + if (instate.greeting === undefined) + instate.greeting = data.toString('binary', start, end); + else + instate.greeting += data.toString('binary', start, end); + } + if (matched) { + expect.type = undefined; + instate.search.removeListener('info', onInfo); + } + }); + ss.maxMatches = 1; + expectData(this, EXP_TYPE_HEADER); + instate.status = IN_GREETING; + } + } else if (instate.status === IN_GREETING) { + debug('DEBUG: Parser: IN_GREETING'); + instate.search = undefined; + // Retrieve the identification bytes after the "SSH-" header + p = i; + expectData(this, EXP_TYPE_LF); + instate.status = IN_HEADER; + } else if (instate.status === IN_HEADER) { + debug('DEBUG: Parser: IN_HEADER'); + if (buffer.charCodeAt(buffer.length - 1) === 13) + buffer = buffer.slice(0, -1); + var idxDash = buffer.indexOf('-'); + var idxSpace = buffer.indexOf(' '); + var header = { + // RFC says greeting SHOULD be utf8 + greeting: instate.greeting, + identRaw: 'SSH-' + buffer, + versions: { + protocol: buffer.substr(0, idxDash), + software: (idxSpace === -1 + ? buffer.substring(idxDash + 1) + : buffer.substring(idxDash + 1, idxSpace)) + }, + comments: (idxSpace > -1 ? buffer.substring(idxSpace + 1) : undefined) + }; + instate.greeting = undefined; + + if (header.versions.protocol !== '1.99' + && header.versions.protocol !== '2.0') { + this.reset(); + debug('DEBUG: Parser: protocol version not supported: ' + + header.versions.protocol); + return callback(new Error('Protocol version not supported')); + } else + this.emit('header', header); + + if (instate.status === IN_INIT) { + // We reset from an event handler, possibly due to an unsupported SSH + // protocol version? + return; + } + + var identRaw = header.identRaw; + var software = header.versions.software; + this.debug('DEBUG: Remote ident: ' + inspect(identRaw)); + for (var j = 0, rule; j < BUGGY_IMPLS_LEN; ++j) { + rule = BUGGY_IMPLS[j]; + if (typeof rule[0] === 'string') { + if (software === rule[0]) + this.remoteBugs |= rule[1]; + } else if (rule[0].test(software)) + this.remoteBugs |= rule[1]; + } + instate.identRaw = identRaw; + // Adjust bytesReceived first otherwise it will have an incorrectly larger + // total when we call back into this function after completing KEXINIT + this.bytesReceived -= (chlen - i); + KEXINIT(this, function() { + if (i === chlen) + callback(); + else + self._transform(chunk.slice(i), encoding, callback); + }); + instate.status = IN_PACKETBEFORE; + return; + } else if (instate.status === IN_PACKETBEFORE) { + blockLen = (decrypt.instance ? decrypt.info.blockLen : 8); + debug('DEBUG: Parser: IN_PACKETBEFORE (expecting ' + blockLen + ')'); + // Wait for the right number of bytes so we can determine the incoming + // packet length + expectData(this, EXP_TYPE_BYTES, blockLen, decrypt.buf); + instate.status = IN_PACKET; + } else if (instate.status === IN_PACKET) { + debug('DEBUG: Parser: IN_PACKET'); + if (decrypt.instance) { + decryptAuthMode = (decrypt.info.authLen > 0); + if (!decryptAuthMode) + buffer = decryptData(this, buffer); + blockLen = decrypt.info.blockLen; + } else { + decryptAuthMode = false; + blockLen = 8; + } + + r = readInt(buffer, 0, this, callback); + if (r === false) + return; + var hmacInfo = instate.hmac.info; + var macSize; + if (hmacInfo) + macSize = hmacInfo.actualLen; + else + macSize = 0; + var fullPacketLen = r + 4 + macSize; + var maxPayloadLen = this.maxPacketSize; + if (decompress.instance) { + // Account for compressed payloads + // This formula is taken from dropbear which derives it from zlib's + // documentation. Explanation from dropbear: + /* For exact details see http://www.zlib.net/zlib_tech.html + * 5 bytes per 16kB block, plus 6 bytes for the stream. + * We might allocate 5 unnecessary bytes here if it's an + * exact multiple. */ + maxPayloadLen += (((this.maxPacketSize / 16384) + 1) * 5 + 6); + } + if (r > maxPayloadLen + // TODO: Change 16 to "MAX(16, decrypt.info.blockLen)" when/if SSH2 + // adopts 512-bit ciphers + || fullPacketLen < (16 + macSize) + || ((r + (decryptAuthMode ? 0 : 4)) % blockLen) !== 0) { + this.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR); + debug('DEBUG: Parser: Bad packet length (' + fullPacketLen + ')'); + return callback(new Error('Bad packet length')); + } + + instate.pktLen = r; + var remainLen = instate.pktLen + 4 - blockLen; + if (decryptAuthMode) { + decrypt.instance.setAAD(buffer.slice(0, 4)); + debug('DEBUG: Parser: pktLen:' + + instate.pktLen + + ',remainLen:' + + remainLen); + } else { + instate.padLen = buffer[4]; + debug('DEBUG: Parser: pktLen:' + + instate.pktLen + + ',padLen:' + + instate.padLen + + ',remainLen:' + + remainLen); + } + if (remainLen > 0) { + if (decryptAuthMode) + instate.pktExtra = buffer.slice(4); + else + instate.pktExtra = buffer.slice(5); + // Grab the rest of the packet + expectData(this, EXP_TYPE_BYTES, remainLen); + instate.status = IN_PACKETDATA; + } else if (remainLen < 0) + instate.status = IN_PACKETBEFORE; + else { + // Entire message fit into one block + skipDecrypt = true; + instate.status = IN_PACKETDATA; + continue; + } + } else if (instate.status === IN_PACKETDATA) { + debug('DEBUG: Parser: IN_PACKETDATA'); + if (decrypt.instance) { + decryptAuthMode = (decrypt.info.authLen > 0); + if (!skipDecrypt) { + if (!decryptAuthMode) + buffer = decryptData(this, buffer); + } else { + skipDecrypt = false; + } + } else { + decryptAuthMode = false; + skipDecrypt = false; + } + var padStart = instate.pktLen - instate.padLen - 1; + // TODO: Allocate a Buffer once that is slightly larger than maxPacketSize + // (to accommodate for packet length field and MAC) and re-use that + // instead + if (instate.pktExtra) { + buf = Buffer.allocUnsafe(instate.pktExtra.length + buffer.length); + instate.pktExtra.copy(buf); + buffer.copy(buf, instate.pktExtra.length); + instate.payload = buf.slice(0, padStart); + } else { + // Entire message fit into one block + if (decryptAuthMode) + buf = buffer.slice(4); + else + buf = buffer.slice(5); + instate.payload = buffer.slice(5, 5 + padStart); + } + if (instate.hmac.info !== undefined) { + // Wait for hmac hash + var inHMACSize = decrypt.info.authLen || instate.hmac.info.actualLen; + debug('DEBUG: Parser: HMAC size:' + inHMACSize); + expectData(this, EXP_TYPE_BYTES, inHMACSize, instate.hmac.buf); + instate.status = IN_PACKETDATAVERIFY; + instate.packet = buf; + } else + instate.status = IN_PACKETDATAAFTER; + instate.pktExtra = undefined; + buf = undefined; + } else if (instate.status === IN_PACKETDATAVERIFY) { + debug('DEBUG: Parser: IN_PACKETDATAVERIFY'); + // Verify packet data integrity + if (hmacVerify(this, buffer)) { + debug('DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)'); + instate.status = IN_PACKETDATAAFTER; + instate.packet = undefined; + } else { + this.reset(); + debug('DEBUG: Parser: IN_PACKETDATAVERIFY (Invalid HMAC)'); + return callback(new Error('Invalid HMAC')); + } + } else if (instate.status === IN_PACKETDATAAFTER) { + if (decompress.instance) { + if (!decomp) { + debug('DEBUG: Parser: Decompressing'); + decompress.instance.write(instate.payload); + var decompBuf = []; + var decompBufLen = 0; + decompress.instance.on('readable', function() { + var buf; + while (buf = this.read()) { + decompBuf.push(buf); + decompBufLen += buf.length; + } + }).flush(Z_PARTIAL_FLUSH, function() { + decompress.instance.removeAllListeners('readable'); + if (decompBuf.length === 1) + instate.payload = decompBuf[0]; + else + instate.payload = Buffer.concat(decompBuf, decompBufLen); + decompBuf = null; + var nextSlice; + if (i === chlen) + nextSlice = EMPTY_BUFFER; // Avoid slicing a zero-length buffer + else + nextSlice = chunk.slice(i); + self._transform(nextSlice, encoding, callback, true); + }); + return; + } else { + // Make sure we reset this after this first time in the loop, + // otherwise we could end up trying to interpret as-is another + // compressed packet that is within the same chunk + decomp = false; + } + } + + this.emit('packet'); + + var ptype = instate.payload[0]; + + if (debug !== DEBUG_NOOP) { + var msgPacket = 'DEBUG: Parser: IN_PACKETDATAAFTER, packet: '; + var authMethod = state.authsQueue[0]; + var msgPktType = null; + + if (outstate.status === OUT_REKEYING + && !(ptype <= 4 || (ptype >= 20 && ptype <= 49))) + msgPacket += '(enqueued) '; + + if (ptype === MESSAGE.KEXDH_INIT) { + switch (state.kex.type) { + case 'group': + msgPktType = 'KEXDH_INIT'; + break; + case 'groupex': + msgPktType = 'KEXDH_GEX_REQUEST'; + break; + default: + msgPktType = 'KEXECDH_INIT'; + } + } else if (ptype === MESSAGE.KEXDH_REPLY) { + switch (state.kex.type) { + case 'group': + msgPktType = 'KEXDH_REPLY'; + break; + case 'groupex': + msgPktType = 'KEXDH_GEX_GROUP'; + break; + default: + msgPktType = 'KEXECDH_REPLY'; + } + } else if (ptype === MESSAGE.KEXDH_GEX_GROUP) { + msgPktType = 'KEXDH_GEX_GROUP'; + } else if (ptype === MESSAGE.KEXDH_GEX_REPLY) { + msgPktType = 'KEXDH_GEX_REPLY'; + } else if (ptype === 60) { + if (authMethod === 'password') + msgPktType = 'USERAUTH_PASSWD_CHANGEREQ'; + else if (authMethod === 'keyboard-interactive') + msgPktType = 'USERAUTH_INFO_REQUEST'; + else if (authMethod === 'publickey') + msgPktType = 'USERAUTH_PK_OK'; + else + msgPktType = 'UNKNOWN PACKET 60'; + } else if (ptype === 61) { + if (authMethod === 'keyboard-interactive') + msgPktType = 'USERAUTH_INFO_RESPONSE'; + else + msgPktType = 'UNKNOWN PACKET 61'; + } + + if (msgPktType === null) + msgPktType = MESSAGE[ptype]; + + // Don't write debug output for messages we custom make in parsePacket() + if (ptype !== MESSAGE.CHANNEL_OPEN + && ptype !== MESSAGE.CHANNEL_REQUEST + && ptype !== MESSAGE.CHANNEL_SUCCESS + && ptype !== MESSAGE.CHANNEL_FAILURE + && ptype !== MESSAGE.CHANNEL_EOF + && ptype !== MESSAGE.CHANNEL_CLOSE + && ptype !== MESSAGE.CHANNEL_DATA + && ptype !== MESSAGE.CHANNEL_EXTENDED_DATA + && ptype !== MESSAGE.CHANNEL_WINDOW_ADJUST + && ptype !== MESSAGE.DISCONNECT + && ptype !== MESSAGE.USERAUTH_REQUEST + && ptype !== MESSAGE.GLOBAL_REQUEST) + debug(msgPacket + msgPktType); + } + + // Only parse packet if we are not re-keying or the packet is not a + // transport layer packet needed for re-keying + if (outstate.status === OUT_READY + || ptype <= 4 + || (ptype >= 20 && ptype <= 49)) { + if (parsePacket(this, callback) === false) + return; + + if (instate.status === IN_INIT) { + // We were reset due to some error/disagreement ? + return; + } + } else if (outstate.status === OUT_REKEYING) { + if (instate.rekeyQueue.length === MAX_PACKETS_REKEYING) { + debug('DEBUG: Parser: Max incoming re-key queue length reached'); + this.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR); + return callback( + new Error('Incoming re-key queue length limit reached') + ); + } + + // Make sure to record the sequence number in case we need it later on + // when we drain the queue (e.g. unknown packet) + var seqno = instate.seqno; + if (++instate.seqno > MAX_SEQNO) + instate.seqno = 0; + + instate.rekeyQueue.push([seqno, instate.payload]); + } + + instate.status = IN_PACKETBEFORE; + instate.payload = undefined; + } + if (buffer !== undefined) + buffer = undefined; + } + + callback(); +}; + +SSH2Stream.prototype.reset = function(noend) { + if (this._state) { + var state = this._state; + state.incoming.status = IN_INIT; + state.outgoing.status = OUT_INIT; + } else { + this._state = { + authsQueue: [], + hostkeyFormat: undefined, + kex: undefined, + + incoming: { + status: IN_INIT, + expectedPacket: undefined, + search: undefined, + greeting: undefined, + seqno: 0, + pktLen: undefined, + padLen: undefined, + pktExtra: undefined, + payload: undefined, + packet: undefined, + kexinit: undefined, + identRaw: undefined, + rekeyQueue: [], + ignoreNext: false, + + expect: { + amount: undefined, + type: undefined, + ptr: 0, + buf: undefined + }, + + decrypt: { + instance: false, + info: undefined, + iv: undefined, + key: undefined, + buf: undefined, + type: undefined + }, + + hmac: { + info: undefined, + key: undefined, + buf: undefined, + type: false + }, + + decompress: { + instance: false, + type: false + } + }, + + outgoing: { + status: OUT_INIT, + seqno: 0, + bufSeqno: Buffer.allocUnsafe(4), + rekeyQueue: [], + kexinit: undefined, + kexsecret: undefined, + pubkey: undefined, + exchangeHash: undefined, + sessionId: undefined, + sentNEWKEYS: false, + + encrypt: { + instance: false, + info: undefined, + iv: undefined, + key: undefined, + type: undefined + }, + + hmac: { + info: undefined, + key: undefined, + buf: undefined, + type: false + }, + + compress: { + instance: false, + type: false, + queue: null + } + } + }; + } + if (!noend) { + if (this.readable) + this.push(null); + } +}; + +// Common methods +// Global +SSH2Stream.prototype.disconnect = function(reason) { + /* + byte SSH_MSG_DISCONNECT + uint32 reason code + string description in ISO-10646 UTF-8 encoding + string language tag + */ + var buf = Buffer.alloc(1 + 4 + 4 + 4); + + buf[0] = MESSAGE.DISCONNECT; + + if (DISCONNECT_REASON[reason] === undefined) + reason = DISCONNECT_REASON.BY_APPLICATION; + writeUInt32BE(buf, reason, 1); + + this.debug('DEBUG: Outgoing: Writing DISCONNECT (' + + DISCONNECT_REASON[reason] + + ')'); + send(this, buf); + this.reset(); + + return false; +}; +SSH2Stream.prototype.ping = function() { + this.debug('DEBUG: Outgoing: Writing ping (GLOBAL_REQUEST: keepalive@openssh.com)'); + return send(this, PING_PACKET); +}; +SSH2Stream.prototype.rekey = function() { + var status = this._state.outgoing.status; + if (status === OUT_REKEYING) + throw new Error('A re-key is already in progress'); + else if (status !== OUT_READY) + throw new Error('Cannot re-key yet'); + + this.debug('DEBUG: Outgoing: Starting re-key'); + return KEXINIT(this); +}; + +// 'ssh-connection' service-specific +SSH2Stream.prototype.requestSuccess = function(data) { + var buf; + if (Buffer.isBuffer(data)) { + buf = Buffer.allocUnsafe(1 + data.length); + + buf[0] = MESSAGE.REQUEST_SUCCESS; + + data.copy(buf, 1); + } else + buf = REQUEST_SUCCESS_PACKET; + + this.debug('DEBUG: Outgoing: Writing REQUEST_SUCCESS'); + return send(this, buf); +}; +SSH2Stream.prototype.requestFailure = function() { + this.debug('DEBUG: Outgoing: Writing REQUEST_FAILURE'); + return send(this, REQUEST_FAILURE_PACKET); +}; +SSH2Stream.prototype.channelSuccess = function(chan) { + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4); + + buf[0] = MESSAGE.CHANNEL_SUCCESS; + + writeUInt32BE(buf, chan, 1); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_SUCCESS (' + chan + ')'); + return send(this, buf); +}; +SSH2Stream.prototype.channelFailure = function(chan) { + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4); + + buf[0] = MESSAGE.CHANNEL_FAILURE; + + writeUInt32BE(buf, chan, 1); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_FAILURE (' + chan + ')'); + return send(this, buf); +}; +SSH2Stream.prototype.channelEOF = function(chan) { + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4); + + buf[0] = MESSAGE.CHANNEL_EOF; + + writeUInt32BE(buf, chan, 1); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_EOF (' + chan + ')'); + return send(this, buf); +}; +SSH2Stream.prototype.channelClose = function(chan) { + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4); + + buf[0] = MESSAGE.CHANNEL_CLOSE; + + writeUInt32BE(buf, chan, 1); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_CLOSE (' + chan + ')'); + return send(this, buf); +}; +SSH2Stream.prototype.channelWindowAdjust = function(chan, amount) { + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4 + 4); + + buf[0] = MESSAGE.CHANNEL_WINDOW_ADJUST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, amount, 5); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_WINDOW_ADJUST (' + + chan + + ', ' + + amount + + ')'); + return send(this, buf); +}; +SSH2Stream.prototype.channelData = function(chan, data) { + var dataIsBuffer = Buffer.isBuffer(data); + var dataLen = (dataIsBuffer ? data.length : Buffer.byteLength(data)); + var buf = Buffer.allocUnsafe(1 + 4 + 4 + dataLen); + + buf[0] = MESSAGE.CHANNEL_DATA; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, dataLen, 5); + if (dataIsBuffer) + data.copy(buf, 9); + else + buf.write(data, 9, dataLen, 'utf8'); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_DATA (' + chan + ')'); + return send(this, buf); +}; +SSH2Stream.prototype.channelExtData = function(chan, data, type) { + var dataIsBuffer = Buffer.isBuffer(data); + var dataLen = (dataIsBuffer ? data.length : Buffer.byteLength(data)); + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 4 + dataLen); + + buf[0] = MESSAGE.CHANNEL_EXTENDED_DATA; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, type, 5); + + writeUInt32BE(buf, dataLen, 9); + if (dataIsBuffer) + data.copy(buf, 13); + else + buf.write(data, 13, dataLen, 'utf8'); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_EXTENDED_DATA (' + chan + ')'); + return send(this, buf); +}; +SSH2Stream.prototype.channelOpenConfirm = function(remoteChan, localChan, + initWindow, maxPacket) { + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 4 + 4); + + buf[0] = MESSAGE.CHANNEL_OPEN_CONFIRMATION; + + writeUInt32BE(buf, remoteChan, 1); + + writeUInt32BE(buf, localChan, 5); + + writeUInt32BE(buf, initWindow, 9); + + writeUInt32BE(buf, maxPacket, 13); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN_CONFIRMATION (r:' + + remoteChan + + ', l:' + + localChan + + ')'); + return send(this, buf); +}; +SSH2Stream.prototype.channelOpenFail = function(remoteChan, reason, desc, + lang) { + if (typeof desc !== 'string') + desc = ''; + if (typeof lang !== 'string') + lang = ''; + + var descLen = Buffer.byteLength(desc); + var langLen = Buffer.byteLength(lang); + var p = 9; + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 4 + descLen + 4 + langLen); + + buf[0] = MESSAGE.CHANNEL_OPEN_FAILURE; + + writeUInt32BE(buf, remoteChan, 1); + + writeUInt32BE(buf, reason, 5); + + writeUInt32BE(buf, descLen, p); + p += 4; + if (descLen) { + buf.write(desc, p, descLen, 'utf8'); + p += descLen; + } + + writeUInt32BE(buf, langLen, p); + if (langLen) + buf.write(lang, p += 4, langLen, 'ascii'); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN_FAILURE (' + + remoteChan + + ')'); + return send(this, buf); +}; + +// Client-specific methods +// Global +SSH2Stream.prototype.service = function(svcName) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var svcNameLen = Buffer.byteLength(svcName); + var buf = Buffer.allocUnsafe(1 + 4 + svcNameLen); + + buf[0] = MESSAGE.SERVICE_REQUEST; + + writeUInt32BE(buf, svcNameLen, 1); + buf.write(svcName, 5, svcNameLen, 'ascii'); + + this.debug('DEBUG: Outgoing: Writing SERVICE_REQUEST (' + svcName + ')'); + return send(this, buf); +}; +// 'ssh-connection' service-specific +SSH2Stream.prototype.tcpipForward = function(bindAddr, bindPort, wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var addrlen = Buffer.byteLength(bindAddr); + var buf = Buffer.allocUnsafe(1 + 4 + 13 + 1 + 4 + addrlen + 4); + + buf[0] = MESSAGE.GLOBAL_REQUEST; + + writeUInt32BE(buf, 13, 1); + buf.write('tcpip-forward', 5, 13, 'ascii'); + + buf[18] = (wantReply === undefined || wantReply === true ? 1 : 0); + + writeUInt32BE(buf, addrlen, 19); + buf.write(bindAddr, 23, addrlen, 'ascii'); + + writeUInt32BE(buf, bindPort, 23 + addrlen); + + this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (tcpip-forward)'); + return send(this, buf); +}; +SSH2Stream.prototype.cancelTcpipForward = function(bindAddr, bindPort, + wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var addrlen = Buffer.byteLength(bindAddr); + var buf = Buffer.allocUnsafe(1 + 4 + 20 + 1 + 4 + addrlen + 4); + + buf[0] = MESSAGE.GLOBAL_REQUEST; + + writeUInt32BE(buf, 20, 1); + buf.write('cancel-tcpip-forward', 5, 20, 'ascii'); + + buf[25] = (wantReply === undefined || wantReply === true ? 1 : 0); + + writeUInt32BE(buf, addrlen, 26); + buf.write(bindAddr, 30, addrlen, 'ascii'); + + writeUInt32BE(buf, bindPort, 30 + addrlen); + + this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (cancel-tcpip-forward)'); + return send(this, buf); +}; +SSH2Stream.prototype.openssh_streamLocalForward = function(socketPath, + wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var pathlen = Buffer.byteLength(socketPath); + var buf = Buffer.allocUnsafe(1 + 4 + 31 + 1 + 4 + pathlen); + + buf[0] = MESSAGE.GLOBAL_REQUEST; + + writeUInt32BE(buf, 31, 1); + buf.write('streamlocal-forward@openssh.com', 5, 31, 'ascii'); + + buf[36] = (wantReply === undefined || wantReply === true ? 1 : 0); + + writeUInt32BE(buf, pathlen, 37); + buf.write(socketPath, 41, pathlen, 'utf8'); + + this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (streamlocal-forward@openssh.com)'); + return send(this, buf); +}; +SSH2Stream.prototype.openssh_cancelStreamLocalForward = function(socketPath, + wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var pathlen = Buffer.byteLength(socketPath); + var buf = Buffer.allocUnsafe(1 + 4 + 38 + 1 + 4 + pathlen); + + buf[0] = MESSAGE.GLOBAL_REQUEST; + + writeUInt32BE(buf, 38, 1); + buf.write('cancel-streamlocal-forward@openssh.com', 5, 38, 'ascii'); + + buf[43] = (wantReply === undefined || wantReply === true ? 1 : 0); + + writeUInt32BE(buf, pathlen, 44); + buf.write(socketPath, 48, pathlen, 'utf8'); + + this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (cancel-streamlocal-forward@openssh.com)'); + return send(this, buf); +}; +SSH2Stream.prototype.directTcpip = function(chan, initWindow, maxPacket, cfg) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var srclen = Buffer.byteLength(cfg.srcIP); + var dstlen = Buffer.byteLength(cfg.dstIP); + var p = 29; + var buf = Buffer.allocUnsafe(1 + 4 + 12 + 4 + 4 + 4 + 4 + srclen + 4 + 4 + + dstlen + 4); + + buf[0] = MESSAGE.CHANNEL_OPEN; + + writeUInt32BE(buf, 12, 1); + buf.write('direct-tcpip', 5, 12, 'ascii'); + + writeUInt32BE(buf, chan, 17); + + writeUInt32BE(buf, initWindow, 21); + + writeUInt32BE(buf, maxPacket, 25); + + writeUInt32BE(buf, dstlen, p); + buf.write(cfg.dstIP, p += 4, dstlen, 'ascii'); + + writeUInt32BE(buf, cfg.dstPort, p += dstlen); + + writeUInt32BE(buf, srclen, p += 4); + buf.write(cfg.srcIP, p += 4, srclen, 'ascii'); + + writeUInt32BE(buf, cfg.srcPort, p += srclen); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN (' + + chan + + ', direct-tcpip)'); + return send(this, buf); +}; +SSH2Stream.prototype.openssh_directStreamLocal = function(chan, initWindow, + maxPacket, cfg) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var pathlen = Buffer.byteLength(cfg.socketPath); + var p = 47; + var buf = Buffer.allocUnsafe(1 + 4 + 30 + 4 + 4 + 4 + 4 + pathlen + 4 + 4); + + buf[0] = MESSAGE.CHANNEL_OPEN; + + writeUInt32BE(buf, 30, 1); + buf.write('direct-streamlocal@openssh.com', 5, 30, 'ascii'); + + writeUInt32BE(buf, chan, 35); + + writeUInt32BE(buf, initWindow, 39); + + writeUInt32BE(buf, maxPacket, 43); + + writeUInt32BE(buf, pathlen, p); + buf.write(cfg.socketPath, p += 4, pathlen, 'utf8'); + + // reserved fields (string and uint32) + buf.fill(0, buf.length - 8); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN (' + + chan + + ', direct-streamlocal@openssh.com)'); + return send(this, buf); +}; +SSH2Stream.prototype.openssh_noMoreSessions = function(wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var buf = Buffer.allocUnsafe(1 + 4 + 28 + 1); + + buf[0] = MESSAGE.GLOBAL_REQUEST; + + writeUInt32BE(buf, 28, 1); + buf.write('no-more-sessions@openssh.com', 5, 28, 'ascii'); + + buf[33] = (wantReply === undefined || wantReply === true ? 1 : 0); + + this.debug('DEBUG: Outgoing: Writing GLOBAL_REQUEST (no-more-sessions@openssh.com)'); + return send(this, buf); +}; +SSH2Stream.prototype.session = function(chan, initWindow, maxPacket) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4 + 7 + 4 + 4 + 4); + + buf[0] = MESSAGE.CHANNEL_OPEN; + + writeUInt32BE(buf, 7, 1); + buf.write('session', 5, 7, 'ascii'); + + writeUInt32BE(buf, chan, 12); + + writeUInt32BE(buf, initWindow, 16); + + writeUInt32BE(buf, maxPacket, 20); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN (' + + chan + + ', session)'); + return send(this, buf); +}; +SSH2Stream.prototype.windowChange = function(chan, rows, cols, height, width) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 13 + 1 + 4 + 4 + 4 + 4); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 13, 5); + buf.write('window-change', 9, 13, 'ascii'); + + buf[22] = 0; + + writeUInt32BE(buf, cols, 23); + + writeUInt32BE(buf, rows, 27); + + writeUInt32BE(buf, width, 31); + + writeUInt32BE(buf, height, 35); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', window-change)'); + return send(this, buf); +}; +SSH2Stream.prototype.pty = function(chan, rows, cols, height, + width, term, modes, wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + if (!term || !term.length) + term = 'vt100'; + if (modes + && !Buffer.isBuffer(modes) + && !Array.isArray(modes) + && typeof modes === 'object') + modes = modesToBytes(modes); + if (!modes || !modes.length) + modes = NO_TERMINAL_MODES_BUFFER; + + var termLen = term.length; + var modesLen = modes.length; + var p = 21; + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 7 + 1 + 4 + termLen + 4 + 4 + 4 + 4 + + 4 + modesLen); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 7, 5); + buf.write('pty-req', 9, 7, 'ascii'); + + buf[16] = (wantReply === undefined || wantReply === true ? 1 : 0); + + writeUInt32BE(buf, termLen, 17); + buf.write(term, 21, termLen, 'utf8'); + + writeUInt32BE(buf, cols, p += termLen); + + writeUInt32BE(buf, rows, p += 4); + + writeUInt32BE(buf, width, p += 4); + + writeUInt32BE(buf, height, p += 4); + + writeUInt32BE(buf, modesLen, p += 4); + p += 4; + if (Array.isArray(modes)) { + for (var i = 0; i < modesLen; ++i) + buf[p++] = modes[i]; + } else if (Buffer.isBuffer(modes)) { + modes.copy(buf, p); + } + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', pty-req)'); + return send(this, buf); +}; +SSH2Stream.prototype.shell = function(chan, wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 5 + 1); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 5, 5); + buf.write('shell', 9, 5, 'ascii'); + + buf[14] = (wantReply === undefined || wantReply === true ? 1 : 0); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', shell)'); + return send(this, buf); +}; +SSH2Stream.prototype.exec = function(chan, cmd, wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + var cmdlen = (Buffer.isBuffer(cmd) ? cmd.length : Buffer.byteLength(cmd)); + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 4 + 1 + 4 + cmdlen); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 4, 5); + buf.write('exec', 9, 4, 'ascii'); + + buf[13] = (wantReply === undefined || wantReply === true ? 1 : 0); + + writeUInt32BE(buf, cmdlen, 14); + if (Buffer.isBuffer(cmd)) + cmd.copy(buf, 18); + else + buf.write(cmd, 18, cmdlen, 'utf8'); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', exec)'); + return send(this, buf); +}; +SSH2Stream.prototype.signal = function(chan, signal) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + signal = signal.toUpperCase(); + if (signal.slice(0, 3) === 'SIG') + signal = signal.substring(3); + + if (SIGNALS.indexOf(signal) === -1) + throw new Error('Invalid signal: ' + signal); + + var signalLen = signal.length; + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 6 + 1 + 4 + signalLen); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 6, 5); + buf.write('signal', 9, 6, 'ascii'); + + buf[15] = 0; + + writeUInt32BE(buf, signalLen, 16); + buf.write(signal, 20, signalLen, 'ascii'); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', signal)'); + return send(this, buf); +}; +SSH2Stream.prototype.env = function(chan, key, val, wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + var keyLen = Buffer.byteLength(key); + var valLen = (Buffer.isBuffer(val) ? val.length : Buffer.byteLength(val)); + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 3 + 1 + 4 + keyLen + 4 + valLen); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 3, 5); + buf.write('env', 9, 3, 'ascii'); + + buf[12] = (wantReply === undefined || wantReply === true ? 1 : 0); + + writeUInt32BE(buf, keyLen, 13); + buf.write(key, 17, keyLen, 'ascii'); + + writeUInt32BE(buf, valLen, 17 + keyLen); + if (Buffer.isBuffer(val)) + val.copy(buf, 17 + keyLen + 4); + else + buf.write(val, 17 + keyLen + 4, valLen, 'utf8'); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', env)'); + return send(this, buf); +}; +SSH2Stream.prototype.x11Forward = function(chan, cfg, wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + var protolen = Buffer.byteLength(cfg.protocol); + var cookielen = Buffer.byteLength(cfg.cookie); + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 7 + 1 + 1 + 4 + protolen + 4 + + cookielen + 4); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 7, 5); + buf.write('x11-req', 9, 7, 'ascii'); + + buf[16] = (wantReply === undefined || wantReply === true ? 1 : 0); + + buf[17] = (cfg.single ? 1 : 0); + + writeUInt32BE(buf, protolen, 18); + var bp = 22; + if (Buffer.isBuffer(cfg.protocol)) + cfg.protocol.copy(buf, bp); + else + buf.write(cfg.protocol, bp, protolen, 'utf8'); + bp += protolen; + + writeUInt32BE(buf, cookielen, bp); + bp += 4; + if (Buffer.isBuffer(cfg.cookie)) + cfg.cookie.copy(buf, bp); + else + buf.write(cfg.cookie, bp, cookielen, 'binary'); + bp += cookielen; + + writeUInt32BE(buf, (cfg.screen || 0), bp); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', x11-req)'); + return send(this, buf); +}; +SSH2Stream.prototype.subsystem = function(chan, name, wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + var nameLen = Buffer.byteLength(name); + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 9 + 1 + 4 + nameLen); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 9, 5); + buf.write('subsystem', 9, 9, 'ascii'); + + buf[18] = (wantReply === undefined || wantReply === true ? 1 : 0); + + writeUInt32BE(buf, nameLen, 19); + buf.write(name, 23, nameLen, 'ascii'); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', subsystem: ' + + name + + ')'); + return send(this, buf); +}; +SSH2Stream.prototype.openssh_agentForward = function(chan, wantReply) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 26 + 1); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 26, 5); + buf.write('auth-agent-req@openssh.com', 9, 26, 'ascii'); + + buf[35] = (wantReply === undefined || wantReply === true ? 1 : 0); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', auth-agent-req@openssh.com)'); + return send(this, buf); +}; +// 'ssh-userauth' service-specific +SSH2Stream.prototype.authPassword = function(username, password) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var userLen = Buffer.byteLength(username); + var passLen = Buffer.byteLength(password); + var p = 0; + var buf = Buffer.allocUnsafe(1 + + 4 + userLen + + 4 + 14 // "ssh-connection" + + 4 + 8 // "password" + + 1 + + 4 + passLen); + + buf[p] = MESSAGE.USERAUTH_REQUEST; + + writeUInt32BE(buf, userLen, ++p); + buf.write(username, p += 4, userLen, 'utf8'); + + writeUInt32BE(buf, 14, p += userLen); + buf.write('ssh-connection', p += 4, 14, 'ascii'); + + writeUInt32BE(buf, 8, p += 14); + buf.write('password', p += 4, 8, 'ascii'); + + buf[p += 8] = 0; + + writeUInt32BE(buf, passLen, ++p); + buf.write(password, p += 4, passLen, 'utf8'); + + this._state.authsQueue.push('password'); + this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (password)'); + return send(this, buf); +}; +SSH2Stream.prototype.authPK = function(username, pubKey, cbSign) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var self = this; + var outstate = this._state.outgoing; + var keyType; + + if (typeof pubKey.getPublicSSH === 'function') { + keyType = pubKey.type; + pubKey = pubKey.getPublicSSH(); + } else { + keyType = pubKey.toString('ascii', + 4, + 4 + readUInt32BE(pubKey, 0)); + } + + var userLen = Buffer.byteLength(username); + var algoLen = Buffer.byteLength(keyType); + var pubKeyLen = pubKey.length; + var sesLen = outstate.sessionId.length; + var p = 0; + var buf = Buffer.allocUnsafe((cbSign ? 4 + sesLen : 0) + + 1 + + 4 + userLen + + 4 + 14 // "ssh-connection" + + 4 + 9 // "publickey" + + 1 + + 4 + algoLen + + 4 + pubKeyLen + ); + + if (cbSign) { + writeUInt32BE(buf, sesLen, p); + outstate.sessionId.copy(buf, p += 4); + buf[p += sesLen] = MESSAGE.USERAUTH_REQUEST; + } else { + buf[p] = MESSAGE.USERAUTH_REQUEST; + } + + writeUInt32BE(buf, userLen, ++p); + buf.write(username, p += 4, userLen, 'utf8'); + + writeUInt32BE(buf, 14, p += userLen); + buf.write('ssh-connection', p += 4, 14, 'ascii'); + + writeUInt32BE(buf, 9, p += 14); + buf.write('publickey', p += 4, 9, 'ascii'); + + buf[p += 9] = (cbSign ? 1 : 0); + + writeUInt32BE(buf, algoLen, ++p); + buf.write(keyType, p += 4, algoLen, 'ascii'); + + writeUInt32BE(buf, pubKeyLen, p += algoLen); + pubKey.copy(buf, p += 4); + + if (!cbSign) { + this._state.authsQueue.push('publickey'); + this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (publickey -- check)'); + return send(this, buf); + } + + cbSign(buf, function(signature) { + signature = convertSignature(signature, keyType); + if (signature === false) + throw new Error('Error while converting handshake signature'); + + var sigLen = signature.length; + var sigbuf = Buffer.allocUnsafe(1 + + 4 + userLen + + 4 + 14 // "ssh-connection" + + 4 + 9 // "publickey" + + 1 + + 4 + algoLen + + 4 + pubKeyLen + + 4 // 4 + algoLen + 4 + sigLen + + 4 + algoLen + + 4 + sigLen); + + p = 0; + + sigbuf[p] = MESSAGE.USERAUTH_REQUEST; + + writeUInt32BE(sigbuf, userLen, ++p); + sigbuf.write(username, p += 4, userLen, 'utf8'); + + writeUInt32BE(sigbuf, 14, p += userLen); + sigbuf.write('ssh-connection', p += 4, 14, 'ascii'); + + writeUInt32BE(sigbuf, 9, p += 14); + sigbuf.write('publickey', p += 4, 9, 'ascii'); + + sigbuf[p += 9] = 1; + + writeUInt32BE(sigbuf, algoLen, ++p); + sigbuf.write(keyType, p += 4, algoLen, 'ascii'); + + writeUInt32BE(sigbuf, pubKeyLen, p += algoLen); + pubKey.copy(sigbuf, p += 4); + writeUInt32BE(sigbuf, 4 + algoLen + 4 + sigLen, p += pubKeyLen); + writeUInt32BE(sigbuf, algoLen, p += 4); + sigbuf.write(keyType, p += 4, algoLen, 'ascii'); + writeUInt32BE(sigbuf, sigLen, p += algoLen); + signature.copy(sigbuf, p += 4); + + // Servers shouldn't send packet type 60 in response to signed publickey + // attempts, but if they do, interpret as type 60. + self._state.authsQueue.push('publickey'); + self.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (publickey)'); + return send(self, sigbuf); + }); + return true; +}; +SSH2Stream.prototype.authHostbased = function(username, pubKey, hostname, + userlocal, cbSign) { + // TODO: Make DRY by sharing similar code with authPK() + + if (this.server) + throw new Error('Client-only method called in server mode'); + + var self = this; + var outstate = this._state.outgoing; + var keyType; + + if (typeof pubKey.getPublicSSH === 'function') { + keyType = pubKey.type; + pubKey = pubKey.getPublicSSH(); + } else { + keyType = pubKey.toString('ascii', + 4, + 4 + readUInt32BE(pubKey, 0)); + } + + var userLen = Buffer.byteLength(username); + var algoLen = Buffer.byteLength(keyType); + var pubKeyLen = pubKey.length; + var sesLen = outstate.sessionId.length; + var hostnameLen = Buffer.byteLength(hostname); + var userlocalLen = Buffer.byteLength(userlocal); + var p = 0; + var buf = Buffer.allocUnsafe(4 + sesLen + + 1 + + 4 + userLen + + 4 + 14 // "ssh-connection" + + 4 + 9 // "hostbased" + + 4 + algoLen + + 4 + pubKeyLen + + 4 + hostnameLen + + 4 + userlocalLen + ); + + writeUInt32BE(buf, sesLen, p); + outstate.sessionId.copy(buf, p += 4); + + buf[p += sesLen] = MESSAGE.USERAUTH_REQUEST; + + writeUInt32BE(buf, userLen, ++p); + buf.write(username, p += 4, userLen, 'utf8'); + + writeUInt32BE(buf, 14, p += userLen); + buf.write('ssh-connection', p += 4, 14, 'ascii'); + + writeUInt32BE(buf, 9, p += 14); + buf.write('hostbased', p += 4, 9, 'ascii'); + + writeUInt32BE(buf, algoLen, p += 9); + buf.write(keyType, p += 4, algoLen, 'ascii'); + + writeUInt32BE(buf, pubKeyLen, p += algoLen); + pubKey.copy(buf, p += 4); + + writeUInt32BE(buf, hostnameLen, p += pubKeyLen); + buf.write(hostname, p += 4, hostnameLen, 'ascii'); + + writeUInt32BE(buf, userlocalLen, p += hostnameLen); + buf.write(userlocal, p += 4, userlocalLen, 'utf8'); + + cbSign(buf, function(signature) { + signature = convertSignature(signature, keyType); + if (signature === false) + throw new Error('Error while converting handshake signature'); + + var sigLen = signature.length; + var sigbuf = Buffer.allocUnsafe((buf.length - sesLen) + sigLen); + + buf.copy(sigbuf, 0, 4 + sesLen); + writeUInt32BE(sigbuf, sigLen, sigbuf.length - sigLen - 4); + signature.copy(sigbuf, sigbuf.length - sigLen); + + self._state.authsQueue.push('hostbased'); + self.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (hostbased)'); + return send(self, sigbuf); + }); + return true; +}; +SSH2Stream.prototype.authKeyboard = function(username) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var userLen = Buffer.byteLength(username); + var p = 0; + var buf = Buffer.allocUnsafe(1 + + 4 + userLen + + 4 + 14 // "ssh-connection" + + 4 + 20 // "keyboard-interactive" + + 4 // no language set + + 4 // no submethods + ); + + buf[p] = MESSAGE.USERAUTH_REQUEST; + + writeUInt32BE(buf, userLen, ++p); + buf.write(username, p += 4, userLen, 'utf8'); + + writeUInt32BE(buf, 14, p += userLen); + buf.write('ssh-connection', p += 4, 14, 'ascii'); + + writeUInt32BE(buf, 20, p += 14); + buf.write('keyboard-interactive', p += 4, 20, 'ascii'); + + writeUInt32BE(buf, 0, p += 20); + + writeUInt32BE(buf, 0, p += 4); + + this._state.authsQueue.push('keyboard-interactive'); + this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (keyboard-interactive)'); + return send(this, buf); +}; +SSH2Stream.prototype.authNone = function(username) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var userLen = Buffer.byteLength(username); + var p = 0; + var buf = Buffer.allocUnsafe(1 + + 4 + userLen + + 4 + 14 // "ssh-connection" + + 4 + 4 // "none" + ); + + buf[p] = MESSAGE.USERAUTH_REQUEST; + + writeUInt32BE(buf, userLen, ++p); + buf.write(username, p += 4, userLen, 'utf8'); + + writeUInt32BE(buf, 14, p += userLen); + buf.write('ssh-connection', p += 4, 14, 'ascii'); + + writeUInt32BE(buf, 4, p += 14); + buf.write('none', p += 4, 4, 'ascii'); + + this._state.authsQueue.push('none'); + this.debug('DEBUG: Outgoing: Writing USERAUTH_REQUEST (none)'); + return send(this, buf); +}; +SSH2Stream.prototype.authInfoRes = function(responses) { + if (this.server) + throw new Error('Client-only method called in server mode'); + + var responsesLen = 0; + var p = 0; + var resLen; + var len; + var i; + + if (responses) { + for (i = 0, len = responses.length; i < len; ++i) + responsesLen += 4 + Buffer.byteLength(responses[i]); + } + var buf = Buffer.allocUnsafe(1 + 4 + responsesLen); + + buf[p++] = MESSAGE.USERAUTH_INFO_RESPONSE; + + writeUInt32BE(buf, responses ? responses.length : 0, p); + if (responses) { + p += 4; + for (i = 0, len = responses.length; i < len; ++i) { + resLen = Buffer.byteLength(responses[i]); + writeUInt32BE(buf, resLen, p); + p += 4; + if (resLen) { + buf.write(responses[i], p, resLen, 'utf8'); + p += resLen; + } + } + } + + this.debug('DEBUG: Outgoing: Writing USERAUTH_INFO_RESPONSE'); + return send(this, buf); +}; + +// Server-specific methods +// Global +SSH2Stream.prototype.serviceAccept = function(svcName) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var svcNameLen = svcName.length; + var buf = Buffer.allocUnsafe(1 + 4 + svcNameLen); + + buf[0] = MESSAGE.SERVICE_ACCEPT; + + writeUInt32BE(buf, svcNameLen, 1); + buf.write(svcName, 5, svcNameLen, 'ascii'); + + this.debug('DEBUG: Outgoing: Writing SERVICE_ACCEPT (' + svcName + ')'); + send(this, buf); + + if (this.server && this.banner && svcName === 'ssh-userauth') { + /* + byte SSH_MSG_USERAUTH_BANNER + string message in ISO-10646 UTF-8 encoding + string language tag + */ + var bannerLen = Buffer.byteLength(this.banner); + var packetLen = 1 + 4 + bannerLen + 4; + var packet = Buffer.allocUnsafe(packetLen); + packet[0] = MESSAGE.USERAUTH_BANNER; + writeUInt32BE(packet, bannerLen, 1); + packet.write(this.banner, 5, bannerLen, 'utf8'); + packet.fill(0, packetLen - 4); // Empty language tag + this.debug('DEBUG: Outgoing: Writing USERAUTH_BANNER'); + send(this, packet); + this.banner = undefined; // Prevent banner from being displayed again + } +}; +// 'ssh-connection' service-specific +SSH2Stream.prototype.forwardedTcpip = function(chan, initWindow, maxPacket, + cfg) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var boundAddrLen = Buffer.byteLength(cfg.boundAddr); + var remoteAddrLen = Buffer.byteLength(cfg.remoteAddr); + var p = 36 + boundAddrLen; + var buf = Buffer.allocUnsafe(1 + 4 + 15 + 4 + 4 + 4 + 4 + boundAddrLen + 4 + 4 + + remoteAddrLen + 4); + + buf[0] = MESSAGE.CHANNEL_OPEN; + + writeUInt32BE(buf, 15, 1); + buf.write('forwarded-tcpip', 5, 15, 'ascii'); + + writeUInt32BE(buf, chan, 20); + + writeUInt32BE(buf, initWindow, 24); + + writeUInt32BE(buf, maxPacket, 28); + + writeUInt32BE(buf, boundAddrLen, 32); + buf.write(cfg.boundAddr, 36, boundAddrLen, 'ascii'); + + writeUInt32BE(buf, cfg.boundPort, p); + + writeUInt32BE(buf, remoteAddrLen, p += 4); + buf.write(cfg.remoteAddr, p += 4, remoteAddrLen, 'ascii'); + + writeUInt32BE(buf, cfg.remotePort, p += remoteAddrLen); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN (' + + chan + + ', forwarded-tcpip)'); + return send(this, buf); +}; +SSH2Stream.prototype.x11 = function(chan, initWindow, maxPacket, cfg) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var addrLen = Buffer.byteLength(cfg.originAddr); + var p = 24 + addrLen; + var buf = Buffer.allocUnsafe(1 + 4 + 3 + 4 + 4 + 4 + 4 + addrLen + 4); + + buf[0] = MESSAGE.CHANNEL_OPEN; + + writeUInt32BE(buf, 3, 1); + buf.write('x11', 5, 3, 'ascii'); + + writeUInt32BE(buf, chan, 8); + + writeUInt32BE(buf, initWindow, 12); + + writeUInt32BE(buf, maxPacket, 16); + + writeUInt32BE(buf, addrLen, 20); + buf.write(cfg.originAddr, 24, addrLen, 'ascii'); + + writeUInt32BE(buf, cfg.originPort, p); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN (' + + chan + + ', x11)'); + return send(this, buf); +}; +SSH2Stream.prototype.openssh_authAgent = function(chan, initWindow, maxPacket) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var buf = Buffer.allocUnsafe(1 + 4 + 22 + 4 + 4 + 4); + + buf[0] = MESSAGE.CHANNEL_OPEN; + + writeUInt32BE(buf, 22, 1); + buf.write('auth-agent@openssh.com', 5, 22, 'ascii'); + + writeUInt32BE(buf, chan, 27); + + writeUInt32BE(buf, initWindow, 31); + + writeUInt32BE(buf, maxPacket, 35); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN (' + + chan + + ', auth-agent@openssh.com)'); + return send(this, buf); +}; +SSH2Stream.prototype.openssh_forwardedStreamLocal = function(chan, initWindow, + maxPacket, cfg) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var pathlen = Buffer.byteLength(cfg.socketPath); + var buf = Buffer.allocUnsafe(1 + 4 + 33 + 4 + 4 + 4 + 4 + pathlen + 4); + + buf[0] = MESSAGE.CHANNEL_OPEN; + + writeUInt32BE(buf, 33, 1); + buf.write('forwarded-streamlocal@openssh.com', 5, 33, 'ascii'); + + writeUInt32BE(buf, chan, 38); + + writeUInt32BE(buf, initWindow, 42); + + writeUInt32BE(buf, maxPacket, 46); + + writeUInt32BE(buf, pathlen, 50); + buf.write(cfg.socketPath, 54, pathlen, 'utf8'); + + writeUInt32BE(buf, 0, 54 + pathlen); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_OPEN (' + + chan + + ', forwarded-streamlocal@openssh.com)'); + return send(this, buf); +}; +SSH2Stream.prototype.exitStatus = function(chan, status) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + // Does not consume window space + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 11 + 1 + 4); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 11, 5); + buf.write('exit-status', 9, 11, 'ascii'); + + buf[20] = 0; + + writeUInt32BE(buf, status, 21); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', exit-status)'); + return send(this, buf); +}; +SSH2Stream.prototype.exitSignal = function(chan, name, coreDumped, msg) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + // Does not consume window space + var nameLen = Buffer.byteLength(name); + var msgLen = (msg ? Buffer.byteLength(msg) : 0); + var p = 25 + nameLen; + var buf = Buffer.allocUnsafe(1 + 4 + 4 + 11 + 1 + 4 + nameLen + 1 + 4 + msgLen + + 4); + + buf[0] = MESSAGE.CHANNEL_REQUEST; + + writeUInt32BE(buf, chan, 1); + + writeUInt32BE(buf, 11, 5); + buf.write('exit-signal', 9, 11, 'ascii'); + + buf[20] = 0; + + writeUInt32BE(buf, nameLen, 21); + buf.write(name, 25, nameLen, 'utf8'); + + buf[p++] = (coreDumped ? 1 : 0); + + writeUInt32BE(buf, msgLen, p); + p += 4; + if (msgLen) { + buf.write(msg, p, msgLen, 'utf8'); + p += msgLen; + } + + writeUInt32BE(buf, 0, p); + + this.debug('DEBUG: Outgoing: Writing CHANNEL_REQUEST (' + + chan + + ', exit-signal)'); + return send(this, buf); +}; +// 'ssh-userauth' service-specific +SSH2Stream.prototype.authFailure = function(authMethods, isPartial) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var authsQueue = this._state.authsQueue; + if (!authsQueue.length) + throw new Error('No auth in progress'); + + var methods; + + if (typeof authMethods === 'boolean') { + isPartial = authMethods; + authMethods = undefined; + } + + if (authMethods) { + methods = []; + for (var i = 0, len = authMethods.length; i < len; ++i) { + if (authMethods[i].toLowerCase() === 'none') + continue; + methods.push(authMethods[i]); + } + methods = methods.join(','); + } else + methods = ''; + + var methodsLen = methods.length; + var buf = Buffer.allocUnsafe(1 + 4 + methodsLen + 1); + + buf[0] = MESSAGE.USERAUTH_FAILURE; + + writeUInt32BE(buf, methodsLen, 1); + buf.write(methods, 5, methodsLen, 'ascii'); + + buf[5 + methodsLen] = (isPartial === true ? 1 : 0); + + this._state.authsQueue.shift(); + this.debug('DEBUG: Outgoing: Writing USERAUTH_FAILURE'); + return send(this, buf); +}; +SSH2Stream.prototype.authSuccess = function() { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var authsQueue = this._state.authsQueue; + if (!authsQueue.length) + throw new Error('No auth in progress'); + + var state = this._state; + var outstate = state.outgoing; + var instate = state.incoming; + + state.authsQueue.shift(); + + this.debug('DEBUG: Outgoing: Writing USERAUTH_SUCCESS'); + var ret = send(this, USERAUTH_SUCCESS_PACKET); + + if (outstate.compress.type === 'zlib@openssh.com') { + outstate.compress.instance = zlib.createDeflate(ZLIB_OPTS); + outstate.compress.queue = []; + } + if (instate.decompress.type === 'zlib@openssh.com') + instate.decompress.instance = zlib.createInflate(ZLIB_OPTS); + + return ret; +}; +SSH2Stream.prototype.authPKOK = function(keyAlgo, key) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var authsQueue = this._state.authsQueue; + if (!authsQueue.length || authsQueue[0] !== 'publickey') + throw new Error('"publickey" auth not in progress'); + + var keyAlgoLen = keyAlgo.length; + var keyLen = key.length; + var buf = Buffer.allocUnsafe(1 + 4 + keyAlgoLen + 4 + keyLen); + + buf[0] = MESSAGE.USERAUTH_PK_OK; + + writeUInt32BE(buf, keyAlgoLen, 1); + buf.write(keyAlgo, 5, keyAlgoLen, 'ascii'); + + writeUInt32BE(buf, keyLen, 5 + keyAlgoLen); + key.copy(buf, 5 + keyAlgoLen + 4); + + this._state.authsQueue.shift(); + this.debug('DEBUG: Outgoing: Writing USERAUTH_PK_OK'); + return send(this, buf); +}; +SSH2Stream.prototype.authPasswdChg = function(prompt, lang) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var promptLen = Buffer.byteLength(prompt); + var langLen = lang ? lang.length : 0; + var p = 0; + var buf = Buffer.allocUnsafe(1 + 4 + promptLen + 4 + langLen); + + buf[p] = MESSAGE.USERAUTH_PASSWD_CHANGEREQ; + + writeUInt32BE(buf, promptLen, ++p); + buf.write(prompt, p += 4, promptLen, 'utf8'); + + writeUInt32BE(buf, langLen, p += promptLen); + if (langLen) + buf.write(lang, p += 4, langLen, 'ascii'); + + this.debug('DEBUG: Outgoing: Writing USERAUTH_PASSWD_CHANGEREQ'); + return send(this, buf); +}; +SSH2Stream.prototype.authInfoReq = function(name, instructions, prompts) { + if (!this.server) + throw new Error('Server-only method called in client mode'); + + var promptsLen = 0; + var nameLen = name ? Buffer.byteLength(name) : 0; + var instrLen = instructions ? Buffer.byteLength(instructions) : 0; + var p = 0; + var promptLen; + var prompt; + var len; + var i; + + for (i = 0, len = prompts.length; i < len; ++i) + promptsLen += 4 + Buffer.byteLength(prompts[i].prompt) + 1; + var buf = Buffer.allocUnsafe(1 + 4 + nameLen + 4 + instrLen + 4 + 4 + + promptsLen); + + buf[p++] = MESSAGE.USERAUTH_INFO_REQUEST; + + writeUInt32BE(buf, nameLen, p); + p += 4; + if (name) { + buf.write(name, p, nameLen, 'utf8'); + p += nameLen; + } + + writeUInt32BE(buf, instrLen, p); + p += 4; + if (instructions) { + buf.write(instructions, p, instrLen, 'utf8'); + p += instrLen; + } + + writeUInt32BE(buf, 0, p); + p += 4; + + writeUInt32BE(buf, prompts.length, p); + p += 4; + for (i = 0, len = prompts.length; i < len; ++i) { + prompt = prompts[i]; + promptLen = Buffer.byteLength(prompt.prompt); + writeUInt32BE(buf, promptLen, p); + p += 4; + if (promptLen) { + buf.write(prompt.prompt, p, promptLen, 'utf8'); + p += promptLen; + } + buf[p++] = (prompt.echo ? 1 : 0); + } + + this.debug('DEBUG: Outgoing: Writing USERAUTH_INFO_REQUEST'); + return send(this, buf); +}; + +// Shared incoming/parser functions +function onDISCONNECT(self, reason, code, desc, lang) { // Client/Server + if (code !== DISCONNECT_REASON.BY_APPLICATION) { + var err = new Error(desc || reason); + err.code = code; + self.emit('error', err); + } + self.reset(); +} + +function onKEXINIT(self, init, firstFollows) { // Client/Server + var state = self._state; + var outstate = state.outgoing; + + if (outstate.status === OUT_READY) { + self.debug('DEBUG: Received re-key request'); + outstate.status = OUT_REKEYING; + outstate.kexinit = undefined; + KEXINIT(self, check); + } else { + check(); + } + + function check() { + if (check_KEXINIT(self, init, firstFollows) === true) { + if (!self.server) { + if (state.kex.type === 'groupex') + KEXDH_GEX_REQ(self); + else + KEXDH_INIT(self); + } else { + state.incoming.expectedPacket = state.kex.pktInit; + } + } + } +} + +function check_KEXINIT(self, init, firstFollows) { + var state = self._state; + var instate = state.incoming; + var outstate = state.outgoing; + var debug = self.debug; + var serverList; + var clientList; + var val; + var len; + var i; + + debug('DEBUG: Comparing KEXINITs ...'); + + var algos = self.config.algorithms; + + var kexList = algos.kex; + if (self.remoteBugs & BUGS.BAD_DHGEX) { + var copied = false; + for (var j = kexList.length - 1; j >= 0; --j) { + if (kexList[j].indexOf('group-exchange') !== -1) { + if (!copied) { + kexList = kexList.slice(); + copied = true; + } + kexList.splice(j, 1); + } + } + } + + debug('DEBUG: (local) KEX algorithms: ' + kexList); + debug('DEBUG: (remote) KEX algorithms: ' + init.algorithms.kex); + if (self.server) { + serverList = kexList; + clientList = init.algorithms.kex; + } else { + serverList = init.algorithms.kex; + clientList = kexList; + } + // Check for agreeable key exchange algorithm + for (i = 0, len = clientList.length; + i < len && serverList.indexOf(clientList[i]) === -1; + ++i); + if (i === len) { + // No suitable match found! + debug('DEBUG: No matching key exchange algorithm'); + var err = new Error('Handshake failed: no matching key exchange algorithm'); + err.level = 'handshake'; + self.emit('error', err); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + var kex_algorithm = clientList[i]; + debug('DEBUG: KEX algorithm: ' + kex_algorithm); + if (firstFollows + && (!init.algorithms.kex.length + || kex_algorithm !== init.algorithms.kex[0])) { + // Ignore next incoming packet, it was a wrong first guess at KEX algorithm + instate.ignoreNext = true; + } + + debug('DEBUG: (local) Host key formats: ' + algos.serverHostKey); + debug('DEBUG: (remote) Host key formats: ' + init.algorithms.srvHostKey); + if (self.server) { + serverList = algos.serverHostKey; + clientList = init.algorithms.srvHostKey; + } else { + serverList = init.algorithms.srvHostKey; + clientList = algos.serverHostKey; + } + // Check for agreeable server host key format + for (i = 0, len = clientList.length; + i < len && serverList.indexOf(clientList[i]) === -1; + ++i); + if (i === len) { + // No suitable match found! + debug('DEBUG: No matching host key format'); + var err = new Error('Handshake failed: no matching host key format'); + err.level = 'handshake'; + self.emit('error', err); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + state.hostkeyFormat = clientList[i]; + debug('DEBUG: Host key format: ' + state.hostkeyFormat); + + debug('DEBUG: (local) Client->Server ciphers: ' + algos.cipher); + debug('DEBUG: (remote) Client->Server ciphers: ' + + init.algorithms.cs.encrypt); + if (self.server) { + serverList = algos.cipher; + clientList = init.algorithms.cs.encrypt; + } else { + serverList = init.algorithms.cs.encrypt; + clientList = algos.cipher; + } + // Check for agreeable client->server cipher + for (i = 0, len = clientList.length; + i < len && serverList.indexOf(clientList[i]) === -1; + ++i); + if (i === len) { + // No suitable match found! + debug('DEBUG: No matching Client->Server cipher'); + var err = new Error('Handshake failed: no matching client->server cipher'); + err.level = 'handshake'; + self.emit('error', err); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + if (self.server) + val = instate.decrypt.type = clientList[i]; + else + val = outstate.encrypt.type = clientList[i]; + debug('DEBUG: Client->Server Cipher: ' + val); + + debug('DEBUG: (local) Server->Client ciphers: ' + algos.cipher); + debug('DEBUG: (remote) Server->Client ciphers: ' + + (init.algorithms.sc.encrypt)); + if (self.server) { + serverList = algos.cipher; + clientList = init.algorithms.sc.encrypt; + } else { + serverList = init.algorithms.sc.encrypt; + clientList = algos.cipher; + } + // Check for agreeable server->client cipher + for (i = 0, len = clientList.length; + i < len && serverList.indexOf(clientList[i]) === -1; + ++i); + if (i === len) { + // No suitable match found! + debug('DEBUG: No matching Server->Client cipher'); + var err = new Error('Handshake failed: no matching server->client cipher'); + err.level = 'handshake'; + self.emit('error', err); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + if (self.server) + val = outstate.encrypt.type = clientList[i]; + else + val = instate.decrypt.type = clientList[i]; + debug('DEBUG: Server->Client Cipher: ' + val); + + debug('DEBUG: (local) Client->Server HMAC algorithms: ' + algos.hmac); + debug('DEBUG: (remote) Client->Server HMAC algorithms: ' + + init.algorithms.cs.mac); + if (self.server) { + serverList = algos.hmac; + clientList = init.algorithms.cs.mac; + } else { + serverList = init.algorithms.cs.mac; + clientList = algos.hmac; + } + // Check for agreeable client->server hmac algorithm + for (i = 0, len = clientList.length; + i < len && serverList.indexOf(clientList[i]) === -1; + ++i); + if (i === len) { + // No suitable match found! + debug('DEBUG: No matching Client->Server HMAC algorithm'); + var err = new Error('Handshake failed: no matching client->server HMAC'); + err.level = 'handshake'; + self.emit('error', err); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + if (self.server) + val = instate.hmac.type = clientList[i]; + else + val = outstate.hmac.type = clientList[i]; + debug('DEBUG: Client->Server HMAC algorithm: ' + val); + + debug('DEBUG: (local) Server->Client HMAC algorithms: ' + algos.hmac); + debug('DEBUG: (remote) Server->Client HMAC algorithms: ' + + init.algorithms.sc.mac); + if (self.server) { + serverList = algos.hmac; + clientList = init.algorithms.sc.mac; + } else { + serverList = init.algorithms.sc.mac; + clientList = algos.hmac; + } + // Check for agreeable server->client hmac algorithm + for (i = 0, len = clientList.length; + i < len && serverList.indexOf(clientList[i]) === -1; + ++i); + if (i === len) { + // No suitable match found! + debug('DEBUG: No matching Server->Client HMAC algorithm'); + var err = new Error('Handshake failed: no matching server->client HMAC'); + err.level = 'handshake'; + self.emit('error', err); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + if (self.server) + val = outstate.hmac.type = clientList[i]; + else + val = instate.hmac.type = clientList[i]; + debug('DEBUG: Server->Client HMAC algorithm: ' + val); + + debug('DEBUG: (local) Client->Server compression algorithms: ' + + algos.compress); + debug('DEBUG: (remote) Client->Server compression algorithms: ' + + init.algorithms.cs.compress); + if (self.server) { + serverList = algos.compress; + clientList = init.algorithms.cs.compress; + } else { + serverList = init.algorithms.cs.compress; + clientList = algos.compress; + } + // Check for agreeable client->server compression algorithm + for (i = 0, len = clientList.length; + i < len && serverList.indexOf(clientList[i]) === -1; + ++i); + if (i === len) { + // No suitable match found! + debug('DEBUG: No matching Client->Server compression algorithm'); + var err = new Error('Handshake failed: no matching client->server ' + + 'compression algorithm'); + err.level = 'handshake'; + self.emit('error', err); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + if (self.server) + val = instate.decompress.type = clientList[i]; + else + val = outstate.compress.type = clientList[i]; + debug('DEBUG: Client->Server compression algorithm: ' + val); + + debug('DEBUG: (local) Server->Client compression algorithms: ' + + algos.compress); + debug('DEBUG: (remote) Server->Client compression algorithms: ' + + init.algorithms.sc.compress); + if (self.server) { + serverList = algos.compress; + clientList = init.algorithms.sc.compress; + } else { + serverList = init.algorithms.sc.compress; + clientList = algos.compress; + } + // Check for agreeable server->client compression algorithm + for (i = 0, len = clientList.length; + i < len && serverList.indexOf(clientList[i]) === -1; + ++i); + if (i === len) { + // No suitable match found! + debug('DEBUG: No matching Server->Client compression algorithm'); + var err = new Error('Handshake failed: no matching server->client ' + + 'compression algorithm'); + err.level = 'handshake'; + self.emit('error', err); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + if (self.server) + val = outstate.compress.type = clientList[i]; + else + val = instate.decompress.type = clientList[i]; + debug('DEBUG: Server->Client compression algorithm: ' + val); + + state.kex = new KeyExchange(kex_algorithm); + state.kex.generateKeys(); + outstate.pubkey = state.kex.getPublicKey(); + + return true; +} + +function onKEXDH_GEX_GROUP(self, prime, gen) { + var state = self._state; + var outstate = state.outgoing; + + state.kex.setDHParams(prime, gen); + state.kex.generateKeys(); + outstate.pubkey = state.kex.getPublicKey(); + + KEXDH_INIT(self); +} + +function onKEXDH_INIT(self, e) { // Server + KEXDH_REPLY(self, e); +} + +function onKEXDH_REPLY(self, info, verifiedHost) { // Client + var state = self._state; + var instate = state.incoming; + var outstate = state.outgoing; + var debug = self.debug; + var len; + var i; + + if (verifiedHost === undefined) { + instate.expectedPacket = 'NEWKEYS'; + outstate.sentNEWKEYS = false; + + debug('DEBUG: Checking host key format'); + // Ensure all host key formats agree + var hostkey_format = readString(info.hostkey, 0, 'ascii', self); + if (hostkey_format === false) + return false; + if (info.hostkey_format !== state.hostkeyFormat + || info.hostkey_format !== hostkey_format) { + // Expected and actual server host key format do not match! + debug('DEBUG: Host key format mismatch'); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + self.reset(); + var err = new Error('Handshake failed: host key format mismatch'); + err.level = 'handshake'; + self.emit('error', err); + return false; + } + + debug('DEBUG: Checking signature format'); + // Ensure signature formats agree + var sig_format = readString(info.sig, 0, 'ascii', self); + if (sig_format === false) + return false; + if (info.sig_format !== sig_format) { + debug('DEBUG: Signature format mismatch'); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + self.reset(); + var err = new Error('Handshake failed: signature format mismatch'); + err.level = 'handshake'; + self.emit('error', err); + return false; + } + } + + // Verify the host fingerprint first if needed + if (outstate.status === OUT_INIT) { + if (verifiedHost === undefined) { + debug('DEBUG: Verifying host fingerprint'); + var sync = true; + var emitted = self.emit('fingerprint', info.hostkey, function(permitted) { + // Prevent multiple calls to this callback + if (verifiedHost !== undefined) + return; + verifiedHost = !!permitted; + if (!sync) { + // Continue execution by re-entry + onKEXDH_REPLY(self, info, verifiedHost); + } + }); + sync = false; + // Support async calling of verification callback + if (emitted && verifiedHost === undefined) + return; + } + if (verifiedHost === undefined) + debug('DEBUG: Host accepted by default (no verification)'); + else if (verifiedHost === true) + debug('DEBUG: Host accepted (verified)'); + else { + debug('DEBUG: Host denied via fingerprint verification'); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + self.reset(); + var err = new Error('Handshake failed: ' + + 'host fingerprint verification failed'); + err.level = 'handshake'; + self.emit('error', err); + return false; + } + } + + info.pubkey = state.kex.convertPublicKey(info.pubkey); + + info.secret = state.kex.computeSecret(info.pubkey); + + if (info.secret instanceof Error) { + info.secret.message = 'Error while computing DH secret (' + + state.kex.type + '): ' + + info.secret.message; + info.secret.level = 'handshake'; + self.emit('error', info.secret); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + var hash = crypto.createHash(state.kex.hash); + + var len_ident = Buffer.byteLength(self.config.ident); + var len_sident = Buffer.byteLength(instate.identRaw); + var len_init = outstate.kexinit.length; + var len_sinit = instate.kexinit.length; + var len_hostkey = info.hostkey.length; + var len_pubkey = outstate.pubkey.length; + var len_spubkey = info.pubkey.length; + var len_secret = info.secret.length; + + var exchangeBufLen = len_ident + + len_sident + + len_init + + len_sinit + + len_hostkey + + len_pubkey + + len_spubkey + + len_secret + + (4 * 8); // Length fields for above values + + // Group exchange-related + var len_gex_prime; + var len_gex_gen; + var gex_prime; + var gex_gen; + var dhParams = state.kex.getDHParams(); + if (dhParams) { + gex_prime = dhParams.prime; + gex_gen = dhParams.generator; + len_gex_prime = gex_prime.length; + len_gex_gen = gex_gen.length; + exchangeBufLen += (4 * 3); // min, n, max values + exchangeBufLen += (4 * 2); // prime, generator length fields + exchangeBufLen += len_gex_prime; + exchangeBufLen += len_gex_gen; + } + + var bp = 0; + var exchangeBuf = Buffer.allocUnsafe(exchangeBufLen); + + writeUInt32BE(exchangeBuf, len_ident, bp); + bp += 4; + exchangeBuf.write(self.config.ident, bp, 'utf8'); // V_C + bp += len_ident; + + writeUInt32BE(exchangeBuf, len_sident, bp); + bp += 4; + exchangeBuf.write(instate.identRaw, bp, 'utf8'); // V_S + bp += len_sident; + + writeUInt32BE(exchangeBuf, len_init, bp); + bp += 4; + outstate.kexinit.copy(exchangeBuf, bp); // I_C + bp += len_init; + outstate.kexinit = undefined; + + writeUInt32BE(exchangeBuf, len_sinit, bp); + bp += 4; + instate.kexinit.copy(exchangeBuf, bp); // I_S + bp += len_sinit; + instate.kexinit = undefined; + + writeUInt32BE(exchangeBuf, len_hostkey, bp); + bp += 4; + info.hostkey.copy(exchangeBuf, bp); // K_S + bp += len_hostkey; + + if (dhParams) { + KEXDH_GEX_REQ_PACKET.slice(1).copy(exchangeBuf, bp); // min, n, max + bp += (4 * 3); // Skip over bytes just copied + + writeUInt32BE(exchangeBuf, len_gex_prime, bp); + bp += 4; + gex_prime.copy(exchangeBuf, bp); // p + bp += len_gex_prime; + + writeUInt32BE(exchangeBuf, len_gex_gen, bp); + bp += 4; + gex_gen.copy(exchangeBuf, bp); // g + bp += len_gex_gen; + } + + writeUInt32BE(exchangeBuf, len_pubkey, bp); + bp += 4; + outstate.pubkey.copy(exchangeBuf, bp); // e + bp += len_pubkey; + + writeUInt32BE(exchangeBuf, len_spubkey, bp); + bp += 4; + info.pubkey.copy(exchangeBuf, bp); // f + bp += len_spubkey; + + writeUInt32BE(exchangeBuf, len_secret, bp); + bp += 4; + info.secret.copy(exchangeBuf, bp); // K + + outstate.exchangeHash = hash.update(exchangeBuf).digest(); // H + + var rawsig = readString(info.sig, info.sig._pos, self); // s + if (rawsig === false + || !(rawsig = sigSSHToASN1(rawsig, info.sig_format, self))) { + return false; + } + + var hostPubKey = parseDERKey(info.hostkey, info.sig_format); + if (hostPubKey instanceof Error) + return false; + + debug('DEBUG: Verifying signature'); + + if (hostPubKey.verify(outstate.exchangeHash, rawsig) !== true) { + debug('DEBUG: Signature verification failed'); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + self.reset(); + var err = new Error('Handshake failed: signature verification failed'); + err.level = 'handshake'; + self.emit('error', err); + return false; + } + + if (outstate.sessionId === undefined) + outstate.sessionId = outstate.exchangeHash; + outstate.kexsecret = info.secret; + + debug('DEBUG: Outgoing: Writing NEWKEYS'); + if (outstate.status === OUT_REKEYING) + send(self, NEWKEYS_PACKET, undefined, true); + else + send(self, NEWKEYS_PACKET); + outstate.sentNEWKEYS = true; + + if (verifiedHost !== undefined && instate.expectedPacket === undefined) { + // We received NEWKEYS while we were waiting for the fingerprint + // verification callback to be called. In this case we have to re-execute + // onNEWKEYS to finish the handshake. + onNEWKEYS(self); + } +} + +function onNEWKEYS(self) { // Client/Server + var state = self._state; + var outstate = state.outgoing; + var instate = state.incoming; + + instate.expectedPacket = undefined; + + if (!outstate.sentNEWKEYS) + return; + + var len = outstate.kexsecret.length; + + var outCipherInfo = outstate.encrypt.info = CIPHER_INFO[outstate.encrypt.type]; + var p = 0; + + var dhHashAlgo = state.kex.hash; + + var secret = Buffer.allocUnsafe(4 + len); + var iv; + var key; + + // Whenever the client sends a new authentication request, it is enqueued + // here. Once the request is resolved (success, fail, or PK_OK), + // dequeue. Whatever is at the front of the queue determines how we + // interpret packet type 60. + state.authsQueue = []; + + writeUInt32BE(secret, len, p); + p += 4; + outstate.kexsecret.copy(secret, p); + outstate.kexsecret = undefined; + if (!outCipherInfo.stream) { + iv = crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(!self.server ? 'A' : 'B', 'ascii') + .update(outstate.sessionId) + .digest(); + while (iv.length < outCipherInfo.ivLen) { + iv = Buffer.concat([iv, + crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(iv) + .digest()]); + } + if (iv.length > outCipherInfo.ivLen) + iv = iv.slice(0, outCipherInfo.ivLen); + } else { + iv = EMPTY_BUFFER; // Streaming ciphers don't use an IV upfront + } + + key = crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(!self.server ? 'C' : 'D', 'ascii') + .update(outstate.sessionId) + .digest(); + while (key.length < outCipherInfo.keyLen) { + key = Buffer.concat([key, + crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(key) + .digest()]); + } + if (key.length > outCipherInfo.keyLen) + key = key.slice(0, outCipherInfo.keyLen); + + if (outCipherInfo.authLen > 0) { + outstate.encrypt.iv = iv; + outstate.encrypt.key = key; + outstate.encrypt.instance = true; + } else { + var cipherAlgo = SSH_TO_OPENSSL[outstate.encrypt.type]; + outstate.encrypt.instance = crypto.createCipheriv(cipherAlgo, key, iv); + outstate.encrypt.instance.setAutoPadding(false); + } + + // And now for decrypting ... + + var inCipherInfo = instate.decrypt.info = CIPHER_INFO[instate.decrypt.type]; + if (!inCipherInfo.stream) { + iv = crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(!self.server ? 'B' : 'A', 'ascii') + .update(outstate.sessionId) + .digest(); + while (iv.length < inCipherInfo.ivLen) { + iv = Buffer.concat([iv, + crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(iv) + .digest()]); + } + if (iv.length > inCipherInfo.ivLen) + iv = iv.slice(0, inCipherInfo.ivLen); + } else { + iv = EMPTY_BUFFER; // Streaming ciphers don't use an IV upfront + } + + // Create a reusable buffer for decryption purposes + instate.decrypt.buf = Buffer.allocUnsafe(inCipherInfo.blockLen); + + key = crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(!self.server ? 'D' : 'C', 'ascii') + .update(outstate.sessionId) + .digest(); + while (key.length < inCipherInfo.keyLen) { + key = Buffer.concat([key, + crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(key) + .digest()]); + } + if (key.length > inCipherInfo.keyLen) + key = key.slice(0, inCipherInfo.keyLen); + + var decipherAlgo = SSH_TO_OPENSSL[instate.decrypt.type]; + instate.decrypt.instance = crypto.createDecipheriv(decipherAlgo, key, iv); + instate.decrypt.instance.setAutoPadding(false); + instate.decrypt.iv = iv; + instate.decrypt.key = key; + + var emptyBuf; + if (outCipherInfo.discardLen > 0) { + emptyBuf = Buffer.alloc(outCipherInfo.discardLen); + outstate.encrypt.instance.update(emptyBuf); + } + if (inCipherInfo.discardLen > 0) { + if (!emptyBuf || emptyBuf.length !== inCipherInfo.discardLen) + emptyBuf = Buffer.alloc(outCipherInfo.discardLen); + instate.decrypt.instance.update(emptyBuf); + } + + var outHMACInfo = outstate.hmac.info = HMAC_INFO[outstate.hmac.type]; + var inHMACInfo = instate.hmac.info = HMAC_INFO[instate.hmac.type]; + + if (outCipherInfo.authLen === 0) { + key = crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(!self.server ? 'E' : 'F', 'ascii') + .update(outstate.sessionId) + .digest(); + while (key.length < outHMACInfo.len) { + key = Buffer.concat([key, + crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(key) + .digest()]); + } + if (key.length > outHMACInfo.len) + key = key.slice(0, outHMACInfo.len); + outstate.hmac.key = key; + } else { + outstate.hmac.key = undefined; + } + if (inCipherInfo.authLen === 0) { + key = crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(!self.server ? 'F' : 'E', 'ascii') + .update(outstate.sessionId) + .digest(); + while (key.length < inHMACInfo.len) { + key = Buffer.concat([key, + crypto.createHash(dhHashAlgo) + .update(secret) + .update(outstate.exchangeHash) + .update(key) + .digest()]); + } + if (key.length > inHMACInfo.len) + key = key.slice(0, inHMACInfo.len); + instate.hmac.key = key; + } else { + instate.hmac.key = undefined; + } + + // Create a reusable buffer for message verification purposes + var inHMACSize = inCipherInfo.authLen || instate.hmac.info.actualLen; + if (!instate.hmac.buf + || instate.hmac.buf.length !== inHMACSize) { + instate.hmac.buf = Buffer.allocUnsafe(inHMACSize); + } + + outstate.exchangeHash = undefined; + + if (outstate.compress.type === 'zlib') { + outstate.compress.instance = zlib.createDeflate(ZLIB_OPTS); + outstate.compress.queue = []; + } else if (outstate.compress.type === 'none') { + outstate.compress.instance = false; + outstate.compress.queue = null; + } + if (instate.decompress.type === 'zlib') + instate.decompress.instance = zlib.createInflate(ZLIB_OPTS); + else if (instate.decompress.type === 'none') + instate.decompress.instance = false; + + self.bytesSent = self.bytesReceived = 0; + + if (outstate.status === OUT_REKEYING) { + outstate.status = OUT_READY; + + // Empty our outbound buffer of any data we tried to send during the + // re-keying process + var queue = outstate.rekeyQueue; + var qlen = queue.length; + var q = 0; + + outstate.rekeyQueue = []; + + for (; q < qlen; ++q) { + if (Buffer.isBuffer(queue[q])) + send(self, queue[q]); + else + send(self, queue[q][0], queue[q][1]); + } + + // Now empty our inbound buffer of any non-transport layer packets we + // received during the re-keying process + queue = instate.rekeyQueue; + qlen = queue.length; + q = 0; + + instate.rekeyQueue = []; + + var curSeqno = instate.seqno; + for (; q < qlen; ++q) { + instate.seqno = queue[q][0]; + instate.payload = queue[q][1]; + if (parsePacket(self) === false) + return; + + if (instate.status === IN_INIT) { + // We were reset due to some error/disagreement ? + return; + } + } + instate.seqno = curSeqno; + } else { + outstate.status = OUT_READY; + if (instate.status === IN_PACKET) { + // Explicitly update incoming packet parser status in order to get the + // correct decipher, hmac, etc. states. + + // We only get here if the host fingerprint callback was called + // asynchronously and the incoming packet parser is still expecting an + // unencrypted packet, etc. + + self.debug('DEBUG: Parser: IN_PACKETBEFORE (update) (expecting ' + + inCipherInfo.blockLen + ')'); + // Wait for the right number of bytes so we can determine the incoming + // packet length + expectData(self, + EXP_TYPE_BYTES, + inCipherInfo.blockLen, + instate.decrypt.buf); + } + self.emit('ready'); + } +} + +function getPacketType(self, pktType) { + var kex = self._state.kex; + if (kex) { + // Disambiguate + switch (pktType) { + case 30: + return kex.pktInit; + case 31: + switch (kex.type) { + case 'group': + return 'KEXDH_REPLY'; + case 'groupex': + return 'KEXDH_GEX_GROUP'; + default: + return 'KEXECDH_REPLY'; + } + break; + case 33: + if (kex.type === 'groupex') + return 'KEXDH_GEX_REPLY'; + } + } + return MESSAGE[pktType]; +} + +function parsePacket(self, callback) { + var instate = self._state.incoming; + var outstate = self._state.outgoing; + var payload = instate.payload; + var seqno = instate.seqno; + var serviceName; + var lang; + var message; + var info; + var chan; + var data; + var srcIP; + var srcPort; + var sender; + var window; + var packetSize; + var recipient; + var description; + var socketPath; + + if (++instate.seqno > MAX_SEQNO) + instate.seqno = 0; + + if (instate.ignoreNext) { + self.debug('DEBUG: Parser: Packet ignored'); + instate.ignoreNext = false; + return; + } + + var type = payload[0]; + if (type === undefined) + return false; + + // If we receive a packet during handshake that is not the expected packet + // and it is not one of: DISCONNECT, IGNORE, UNIMPLEMENTED, or DEBUG, then we + // close the stream + if (outstate.status !== OUT_READY + && getPacketType(self, type) !== instate.expectedPacket + && type < 1 + && type > 4) { + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, expected: ' + + instate.expectedPacket + + ' but got: ' + + getPacketType(self, type)); + // XXX: Potential issue where the module user decides to initiate a rekey + // via KEXINIT() (which sets `expectedPacket`) after receiving a packet + // and there is still another packet already waiting to be parsed at the + // time the KEXINIT is written. this will cause an unexpected disconnect... + self.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR); + var err = new Error('Received unexpected packet'); + err.level = 'protocol'; + self.emit('error', err); + return false; + } + + if (type === MESSAGE.CHANNEL_DATA) { + /* + byte SSH_MSG_CHANNEL_DATA + uint32 recipient channel + string data + */ + chan = readInt(payload, 1, self, callback); + if (chan === false) + return false; + // TODO: MAX_CHAN_DATA_LEN here should really be dependent upon the + // channel's packet size. The ssh2 module uses 32KB, so we'll hard + // code this for now ... + data = readString(payload, 5, self, callback, 32768); + if (data === false) + return false; + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_DATA (' + + chan + + ')'); + self.emit('CHANNEL_DATA:' + chan, data); + } else if (type === MESSAGE.CHANNEL_EXTENDED_DATA) { + /* + byte SSH_MSG_CHANNEL_EXTENDED_DATA + uint32 recipient channel + uint32 data_type_code + string data + */ + chan = readInt(payload, 1, self, callback); + if (chan === false) + return false; + var dataType = readInt(payload, 5, self, callback); + if (dataType === false) + return false; + data = readString(payload, 9, self, callback); + if (data === false) + return false; + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: ' + + 'CHANNEL_EXTENDED_DATA (' + + chan + + ')'); + self.emit('CHANNEL_EXTENDED_DATA:' + chan, dataType, data); + } else if (type === MESSAGE.CHANNEL_WINDOW_ADJUST) { + /* + byte SSH_MSG_CHANNEL_WINDOW_ADJUST + uint32 recipient channel + uint32 bytes to add + */ + chan = readInt(payload, 1, self, callback); + if (chan === false) + return false; + var bytesToAdd = readInt(payload, 5, self, callback); + if (bytesToAdd === false) + return false; + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: ' + + 'CHANNEL_WINDOW_ADJUST (' + + chan + + ', ' + + bytesToAdd + + ')'); + self.emit('CHANNEL_WINDOW_ADJUST:' + chan, bytesToAdd); + } else if (type === MESSAGE.CHANNEL_SUCCESS) { + /* + byte SSH_MSG_CHANNEL_SUCCESS + uint32 recipient channel + */ + chan = readInt(payload, 1, self, callback); + if (chan === false) + return false; + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_SUCCESS (' + + chan + + ')'); + self.emit('CHANNEL_SUCCESS:' + chan); + } else if (type === MESSAGE.CHANNEL_FAILURE) { + /* + byte SSH_MSG_CHANNEL_FAILURE + uint32 recipient channel + */ + chan = readInt(payload, 1, self, callback); + if (chan === false) + return false; + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_FAILURE (' + + chan + + ')'); + self.emit('CHANNEL_FAILURE:' + chan); + } else if (type === MESSAGE.CHANNEL_EOF) { + /* + byte SSH_MSG_CHANNEL_EOF + uint32 recipient channel + */ + chan = readInt(payload, 1, self, callback); + if (chan === false) + return false; + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_EOF (' + + chan + + ')'); + self.emit('CHANNEL_EOF:' + chan); + } else if (type === MESSAGE.CHANNEL_OPEN) { + /* + byte SSH_MSG_CHANNEL_OPEN + string channel type in US-ASCII only + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + .... channel type specific data follows + */ + var chanType = readString(payload, 1, 'ascii', self, callback); + if (chanType === false) + return false; + sender = readInt(payload, payload._pos, self, callback); + if (sender === false) + return false; + window = readInt(payload, payload._pos += 4, self, callback); + if (window === false) + return false; + packetSize = readInt(payload, payload._pos += 4, self, callback); + if (packetSize === false) + return false; + var channel; + + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_OPEN (' + + sender + + ', ' + + chanType + + ')'); + + if (chanType === 'forwarded-tcpip' // Server->Client + || chanType === 'direct-tcpip') { // Client->Server + /* + string address that was connected / host to connect + uint32 port that was connected / port to connect + string originator IP address + uint32 originator port + */ + var destIP = readString(payload, + payload._pos += 4, + 'ascii', + self, + callback); + if (destIP === false) + return false; + var destPort = readInt(payload, payload._pos, self, callback); + if (destPort === false) + return false; + srcIP = readString(payload, payload._pos += 4, 'ascii', self, callback); + if (srcIP === false) + return false; + srcPort = readInt(payload, payload._pos, self, callback); + if (srcPort === false) + return false; + channel = { + type: chanType, + sender: sender, + window: window, + packetSize: packetSize, + data: { + destIP: destIP, + destPort: destPort, + srcIP: srcIP, + srcPort: srcPort + } + }; + } else if (// Server->Client + chanType === 'forwarded-streamlocal@openssh.com' + // Client->Server + || chanType === 'direct-streamlocal@openssh.com') { + /* + string socket path + string reserved for future use + */ + socketPath = readString(payload, + payload._pos += 4, + 'utf8', + self, + callback); + if (socketPath === false) + return false; + channel = { + type: chanType, + sender: sender, + window: window, + packetSize: packetSize, + data: { + socketPath: socketPath, + } + }; + } else if (chanType === 'x11') { // Server->Client + /* + string originator address (e.g., "192.168.7.38") + uint32 originator port + */ + srcIP = readString(payload, payload._pos += 4, 'ascii', self, callback); + if (srcIP === false) + return false; + srcPort = readInt(payload, payload._pos, self, callback); + if (srcPort === false) + return false; + channel = { + type: chanType, + sender: sender, + window: window, + packetSize: packetSize, + data: { + srcIP: srcIP, + srcPort: srcPort + } + }; + } else { + // 'session' (Client->Server), 'auth-agent@openssh.com' (Server->Client) + channel = { + type: chanType, + sender: sender, + window: window, + packetSize: packetSize, + data: {} + }; + } + + self.emit('CHANNEL_OPEN', channel); + } else if (type === MESSAGE.CHANNEL_OPEN_CONFIRMATION) { + /* + byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION + uint32 recipient channel + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + .... channel type specific data follows + */ + // "The 'recipient channel' is the channel number given in the + // original open request, and 'sender channel' is the channel number + // allocated by the other side." + recipient = readInt(payload, 1, self, callback); + if (recipient === false) + return false; + sender = readInt(payload, 5, self, callback); + if (sender === false) + return false; + window = readInt(payload, 9, self, callback); + if (window === false) + return false; + packetSize = readInt(payload, 13, self, callback); + if (packetSize === false) + return false; + + info = { + recipient: recipient, + sender: sender, + window: window, + packetSize: packetSize + }; + + if (payload.length > 17) + info.data = payload.slice(17); + + self.emit('CHANNEL_OPEN_CONFIRMATION:' + info.recipient, info); + } else if (type === MESSAGE.CHANNEL_OPEN_FAILURE) { + /* + byte SSH_MSG_CHANNEL_OPEN_FAILURE + uint32 recipient channel + uint32 reason code + string description in ISO-10646 UTF-8 encoding + string language tag + */ + recipient = readInt(payload, 1, self, callback); + if (recipient === false) + return false; + var reasonCode = readInt(payload, 5, self, callback); + if (reasonCode === false) + return false; + description = readString(payload, 9, 'utf8', self, callback); + if (description === false) + return false; + lang = readString(payload, payload._pos, 'utf8', self, callback); + if (lang === false) + return false; + payload._pos = 9; + info = { + recipient: recipient, + reasonCode: reasonCode, + reason: CHANNEL_OPEN_FAILURE[reasonCode], + description: description, + lang: lang + }; + + self.emit('CHANNEL_OPEN_FAILURE:' + info.recipient, info); + } else if (type === MESSAGE.CHANNEL_CLOSE) { + /* + byte SSH_MSG_CHANNEL_CLOSE + uint32 recipient channel + */ + chan = readInt(payload, 1, self, callback); + if (chan === false) + return false; + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_CLOSE (' + + chan + + ')'); + self.emit('CHANNEL_CLOSE:' + chan); + } else if (type === MESSAGE.IGNORE) { + /* + byte SSH_MSG_IGNORE + string data + */ + } else if (type === MESSAGE.DISCONNECT) { + /* + byte SSH_MSG_DISCONNECT + uint32 reason code + string description in ISO-10646 UTF-8 encoding + string language tag + */ + var reason = readInt(payload, 1, self, callback); + if (reason === false) + return false; + var reasonText = DISCONNECT_REASON[reason]; + description = readString(payload, 5, 'utf8', self, callback); + if (description === false) + return false; + + if (payload._pos < payload.length) + lang = readString(payload, payload._pos, 'ascii', self, callback); + + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: DISCONNECT (' + + reasonText + + ')'); + + self.emit('DISCONNECT', reasonText, reason, description, lang); + } else if (type === MESSAGE.DEBUG) { + /* + byte SSH_MSG_DEBUG + boolean always_display + string message in ISO-10646 UTF-8 encoding + string language tag + */ + message = readString(payload, 2, 'utf8', self, callback); + if (message === false) + return false; + lang = readString(payload, payload._pos, 'ascii', self, callback); + if (lang === false) + return false; + + self.emit('DEBUG', message, lang); + } else if (type === MESSAGE.NEWKEYS) { + /* + byte SSH_MSG_NEW_KEYS + */ + self.emit('NEWKEYS'); + } else if (type === MESSAGE.SERVICE_REQUEST) { + /* + byte SSH_MSG_SERVICE_REQUEST + string service name + */ + serviceName = readString(payload, 1, 'ascii', self, callback); + if (serviceName === false) + return false; + + self.emit('SERVICE_REQUEST', serviceName); + } else if (type === MESSAGE.SERVICE_ACCEPT) { + /* + byte SSH_MSG_SERVICE_ACCEPT + string service name + */ + serviceName = readString(payload, 1, 'ascii', self, callback); + if (serviceName === false) + return false; + + self.emit('SERVICE_ACCEPT', serviceName); + } else if (type === MESSAGE.USERAUTH_REQUEST) { + /* + byte SSH_MSG_USERAUTH_REQUEST + string user name in ISO-10646 UTF-8 encoding [RFC3629] + string service name in US-ASCII + string method name in US-ASCII + .... method specific fields + */ + var username = readString(payload, 1, 'utf8', self, callback); + if (username === false) + return false; + var svcName = readString(payload, payload._pos, 'ascii', self, callback); + if (svcName === false) + return false; + var method = readString(payload, payload._pos, 'ascii', self, callback); + if (method === false) + return false; + + var methodData; + var methodDesc; + + if (method === 'password') { + methodData = readString(payload, + payload._pos + 1, + 'utf8', + self, + callback); + if (methodData === false) + return false; + } else if (method === 'publickey' || method === 'hostbased') { + var pkSigned; + var keyAlgo; + var key; + var signature; + var blob; + var hostname; + var userlocal; + if (method === 'publickey') { + pkSigned = payload[payload._pos++]; + if (pkSigned === undefined) + return false; + pkSigned = (pkSigned !== 0); + } + keyAlgo = readString(payload, payload._pos, 'ascii', self, callback); + if (keyAlgo === false) + return false; + key = readString(payload, payload._pos, self, callback); + if (key === false) + return false; + + if (pkSigned || method === 'hostbased') { + if (method === 'hostbased') { + hostname = readString(payload, payload._pos, 'ascii', self, callback); + if (hostname === false) + return false; + userlocal = readString(payload, payload._pos, 'utf8', self, callback); + if (userlocal === false) + return false; + } + + var blobEnd = payload._pos; + signature = readString(payload, blobEnd, self, callback); + if (signature === false) + return false; + + if (signature.length > (4 + keyAlgo.length + 4) + && signature.toString('ascii', 4, 4 + keyAlgo.length) === keyAlgo) { + // Skip algoLen + algo + sigLen + signature = signature.slice(4 + keyAlgo.length + 4); + } + + signature = sigSSHToASN1(signature, keyAlgo, self, callback); + if (signature === false) + return false; + + blob = Buffer.allocUnsafe(4 + outstate.sessionId.length + blobEnd); + writeUInt32BE(blob, outstate.sessionId.length, 0); + outstate.sessionId.copy(blob, 4); + payload.copy(blob, 4 + outstate.sessionId.length, 0, blobEnd); + } else { + methodDesc = 'publickey -- check'; + } + + methodData = { + keyAlgo: keyAlgo, + key: key, + signature: signature, + blob: blob, + localHostname: hostname, + localUsername: userlocal + }; + } else if (method === 'keyboard-interactive') { + // Skip language, it's deprecated + var skipLen = readInt(payload, payload._pos, self, callback); + if (skipLen === false) + return false; + methodData = readString(payload, + payload._pos + 4 + skipLen, + 'utf8', + self, + callback); + if (methodData === false) + return false; + } else if (method !== 'none') + methodData = payload.slice(payload._pos); + + if (methodDesc === undefined) + methodDesc = method; + + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: USERAUTH_REQUEST (' + + methodDesc + + ')'); + + self._state.authsQueue.push(method); + self.emit('USERAUTH_REQUEST', username, svcName, method, methodData); + } else if (type === MESSAGE.USERAUTH_SUCCESS) { + /* + byte SSH_MSG_USERAUTH_SUCCESS + */ + if (outstate.compress.type === 'zlib@openssh.com') { + outstate.compress.instance = zlib.createDeflate(ZLIB_OPTS); + outstate.compress.queue = []; + } + if (instate.decompress.type === 'zlib@openssh.com') + instate.decompress.instance = zlib.createInflate(ZLIB_OPTS); + + self._state.authsQueue.shift(); + + self.emit('USERAUTH_SUCCESS'); + } else if (type === MESSAGE.USERAUTH_FAILURE) { + /* + byte SSH_MSG_USERAUTH_FAILURE + name-list authentications that can continue + boolean partial success + */ + var auths = readString(payload, 1, 'ascii', self, callback); + if (auths === false) + return false; + var partSuccess = payload[payload._pos]; + if (partSuccess === undefined) + return false; + + partSuccess = (partSuccess !== 0); + auths = auths.split(','); + + self._state.authsQueue.shift(); + self.emit('USERAUTH_FAILURE', auths, partSuccess); + } else if (type === MESSAGE.USERAUTH_BANNER) { + /* + byte SSH_MSG_USERAUTH_BANNER + string message in ISO-10646 UTF-8 encoding + string language tag + */ + message = readString(payload, 1, 'utf8', self, callback); + if (message === false) + return false; + lang = readString(payload, payload._pos, 'utf8', self, callback); + if (lang === false) + return false; + + self.emit('USERAUTH_BANNER', message, lang); + } else if (type === MESSAGE.GLOBAL_REQUEST) { + /* + byte SSH_MSG_GLOBAL_REQUEST + string request name in US-ASCII only + boolean want reply + .... request-specific data follows + */ + var request = readString(payload, 1, 'ascii', self, callback); + if (request === false) { + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: GLOBAL_REQUEST'); + return false; + } + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: GLOBAL_REQUEST (' + + request + + ')'); + + var wantReply = payload[payload._pos++]; + if (wantReply === undefined) + return false; + wantReply = (wantReply !== 0); + + var reqData; + if (request === 'tcpip-forward' || request === 'cancel-tcpip-forward') { + var bindAddr = readString(payload, payload._pos, 'ascii', self, callback); + if (bindAddr === false) + return false; + var bindPort = readInt(payload, payload._pos, self, callback); + if (bindPort === false) + return false; + reqData = { + bindAddr: bindAddr, + bindPort: bindPort + }; + } else if (request === 'streamlocal-forward@openssh.com' + || request === 'cancel-streamlocal-forward@openssh.com') { + socketPath = readString(payload, payload._pos, 'utf8', self, callback); + if (socketPath === false) + return false; + reqData = { + socketPath: socketPath + }; + } else if (request === 'no-more-sessions@openssh.com') { + // No data + } else { + reqData = payload.slice(payload._pos); + } + + self.emit('GLOBAL_REQUEST', request, wantReply, reqData); + } else if (type === MESSAGE.REQUEST_SUCCESS) { + /* + byte SSH_MSG_REQUEST_SUCCESS + .... response specific data + */ + if (payload.length > 1) + self.emit('REQUEST_SUCCESS', payload.slice(1)); + else + self.emit('REQUEST_SUCCESS'); + } else if (type === MESSAGE.REQUEST_FAILURE) { + /* + byte SSH_MSG_REQUEST_FAILURE + */ + self.emit('REQUEST_FAILURE'); + } else if (type === MESSAGE.UNIMPLEMENTED) { + /* + byte SSH_MSG_UNIMPLEMENTED + uint32 packet sequence number of rejected message + */ + // TODO + } else if (type === MESSAGE.KEXINIT) + return parse_KEXINIT(self, callback); + else if (type === MESSAGE.CHANNEL_REQUEST) + return parse_CHANNEL_REQUEST(self, callback); + else if (type >= 30 && type <= 49) // Key exchange method-specific messages + return parse_KEX(self, type, callback); + else if (type >= 60 && type <= 70) // User auth context-specific messages + return parse_USERAUTH(self, type, callback); + else { + // Unknown packet type + var unimpl = Buffer.allocUnsafe(1 + 4); + unimpl[0] = MESSAGE.UNIMPLEMENTED; + writeUInt32BE(unimpl, seqno, 1); + send(self, unimpl); + } +} + +function parse_KEXINIT(self, callback) { + var instate = self._state.incoming; + var payload = instate.payload; + + /* + byte SSH_MSG_KEXINIT + byte[16] cookie (random bytes) + name-list kex_algorithms + name-list server_host_key_algorithms + name-list encryption_algorithms_client_to_server + name-list encryption_algorithms_server_to_client + name-list mac_algorithms_client_to_server + name-list mac_algorithms_server_to_client + name-list compression_algorithms_client_to_server + name-list compression_algorithms_server_to_client + name-list languages_client_to_server + name-list languages_server_to_client + boolean first_kex_packet_follows + uint32 0 (reserved for future extension) + */ + var init = { + algorithms: { + kex: undefined, + srvHostKey: undefined, + cs: { + encrypt: undefined, + mac: undefined, + compress: undefined + }, + sc: { + encrypt: undefined, + mac: undefined, + compress: undefined + } + }, + languages: { + cs: undefined, + sc: undefined + } + }; + var val; + + val = readList(payload, 17, self, callback); + if (val === false) + return false; + init.algorithms.kex = val; + val = readList(payload, payload._pos, self, callback); + if (val === false) + return false; + init.algorithms.srvHostKey = val; + val = readList(payload, payload._pos, self, callback); + if (val === false) + return false; + init.algorithms.cs.encrypt = val; + val = readList(payload, payload._pos, self, callback); + if (val === false) + return false; + init.algorithms.sc.encrypt = val; + val = readList(payload, payload._pos, self, callback); + if (val === false) + return false; + init.algorithms.cs.mac = val; + val = readList(payload, payload._pos, self, callback); + if (val === false) + return false; + init.algorithms.sc.mac = val; + val = readList(payload, payload._pos, self, callback); + if (val === false) + return false; + init.algorithms.cs.compress = val; + val = readList(payload, payload._pos, self, callback); + if (val === false) + return false; + init.algorithms.sc.compress = val; + val = readList(payload, payload._pos, self, callback); + if (val === false) + return false; + init.languages.cs = val; + val = readList(payload, payload._pos, self, callback); + if (val === false) + return false; + init.languages.sc = val; + + var firstFollows = (payload._pos < payload.length + && payload[payload._pos] === 1); + + instate.kexinit = payload; + + self.emit('KEXINIT', init, firstFollows); +} + +function parse_KEX(self, type, callback) { + var state = self._state; + var instate = state.incoming; + var payload = instate.payload; + + if (state.outgoing.status === OUT_READY + || getPacketType(self, type) !== instate.expectedPacket) { + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, expected: ' + + instate.expectedPacket + + ' but got: ' + + getPacketType(self, type)); + self.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR); + var err = new Error('Received unexpected packet'); + err.level = 'protocol'; + self.emit('error', err); + return false; + } + + if (state.kex.type === 'groupex') { + // Dynamic group exchange-related + + if (self.server) { + // TODO: Support group exchange server-side + self.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR); + var err = new Error('DH group exchange not supported by server'); + err.level = 'handshake'; + self.emit('error', err); + return false; + } else { + if (type === MESSAGE.KEXDH_GEX_GROUP) { + /* + byte SSH_MSG_KEX_DH_GEX_GROUP + mpint p, safe prime + mpint g, generator for subgroup in GF(p) + */ + var prime = readString(payload, 1, self, callback); + if (prime === false) + return false; + var gen = readString(payload, payload._pos, self, callback); + if (gen === false) + return false; + self.emit('KEXDH_GEX_GROUP', prime, gen); + } else if (type === MESSAGE.KEXDH_GEX_REPLY) + return parse_KEXDH_REPLY(self, callback); + } + } else { + // Static group or ECDH-related + + if (type === MESSAGE.KEXDH_INIT) { + /* + byte SSH_MSG_KEXDH_INIT + mpint e + */ + var e = readString(payload, 1, self, callback); + if (e === false) + return false; + + self.emit('KEXDH_INIT', e); + } else if (type === MESSAGE.KEXDH_REPLY) + return parse_KEXDH_REPLY(self, callback); + } +} + +function parse_KEXDH_REPLY(self, callback) { + var payload = self._state.incoming.payload; + /* + byte SSH_MSG_KEXDH_REPLY + / SSH_MSG_KEX_DH_GEX_REPLY + / SSH_MSG_KEX_ECDH_REPLY + string server public host key and certificates (K_S) + mpint f + string signature of H + */ + var hostkey = readString(payload, 1, self, callback); + if (hostkey === false) + return false; + var pubkey = readString(payload, payload._pos, self, callback); + if (pubkey === false) + return false; + var sig = readString(payload, payload._pos, self, callback); + if (sig === false) + return false; + var info = { + hostkey: hostkey, + hostkey_format: undefined, + pubkey: pubkey, + sig: sig, + sig_format: undefined + }; + var hostkey_format = readString(hostkey, 0, 'ascii', self, callback); + if (hostkey_format === false) + return false; + info.hostkey_format = hostkey_format; + var sig_format = readString(sig, 0, 'ascii', self, callback); + if (sig_format === false) + return false; + info.sig_format = sig_format; + self.emit('KEXDH_REPLY', info); +} + +function parse_USERAUTH(self, type, callback) { + var state = self._state; + var authMethod = state.authsQueue[0]; + var payload = state.incoming.payload; + var message; + var lang; + var text; + + if (authMethod === 'password') { + if (type === MESSAGE.USERAUTH_PASSWD_CHANGEREQ) { + /* + byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ + string prompt in ISO-10646 UTF-8 encoding + string language tag + */ + message = readString(payload, 1, 'utf8', self, callback); + if (message === false) + return false; + lang = readString(payload, payload._pos, 'utf8', self, callback); + if (lang === false) + return false; + self.emit('USERAUTH_PASSWD_CHANGEREQ', message, lang); + } + } else if (authMethod === 'keyboard-interactive') { + if (type === MESSAGE.USERAUTH_INFO_REQUEST) { + /* + byte SSH_MSG_USERAUTH_INFO_REQUEST + string name (ISO-10646 UTF-8) + string instruction (ISO-10646 UTF-8) + string language tag -- MAY be empty + int num-prompts + string prompt[1] (ISO-10646 UTF-8) + boolean echo[1] + ... + string prompt[num-prompts] (ISO-10646 UTF-8) + boolean echo[num-prompts] + */ + var name; + var instr; + var nprompts; + + name = readString(payload, 1, 'utf8', self, callback); + if (name === false) + return false; + instr = readString(payload, payload._pos, 'utf8', self, callback); + if (instr === false) + return false; + lang = readString(payload, payload._pos, 'utf8', self, callback); + if (lang === false) + return false; + nprompts = readInt(payload, payload._pos, self, callback); + if (nprompts === false) + return false; + + payload._pos += 4; + + var prompts = []; + for (var prompt = 0; prompt < nprompts; ++prompt) { + text = readString(payload, payload._pos, 'utf8', self, callback); + if (text === false) + return false; + var echo = payload[payload._pos++]; + if (echo === undefined) + return false; + echo = (echo !== 0); + prompts.push({ + prompt: text, + echo: echo + }); + } + self.emit('USERAUTH_INFO_REQUEST', name, instr, lang, prompts); + } else if (type === MESSAGE.USERAUTH_INFO_RESPONSE) { + /* + byte SSH_MSG_USERAUTH_INFO_RESPONSE + int num-responses + string response[1] (ISO-10646 UTF-8) + ... + string response[num-responses] (ISO-10646 UTF-8) + */ + var nresponses = readInt(payload, 1, self, callback); + if (nresponses === false) + return false; + + payload._pos = 5; + + var responses = []; + for (var response = 0; response < nresponses; ++response) { + text = readString(payload, payload._pos, 'utf8', self, callback); + if (text === false) + return false; + responses.push(text); + } + self.emit('USERAUTH_INFO_RESPONSE', responses); + } + } else if (authMethod === 'publickey') { + if (type === MESSAGE.USERAUTH_PK_OK) { + /* + byte SSH_MSG_USERAUTH_PK_OK + string public key algorithm name from the request + string public key blob from the request + */ + var authsQueue = self._state.authsQueue; + if (!authsQueue.length || authsQueue[0] !== 'publickey') + return; + authsQueue.shift(); + self.emit('USERAUTH_PK_OK'); + // XXX: Parse public key info? client currently can ignore it because + // there is only one outstanding auth request at any given time, so it + // knows which key was OK'd + } + } else if (authMethod !== undefined) { + // Invalid packet for this auth type + self.disconnect(DISCONNECT_REASON.PROTOCOL_ERROR); + var err = new Error('Invalid authentication method: ' + authMethod); + err.level = 'protocol'; + self.emit('error', err); + } +} + +function parse_CHANNEL_REQUEST(self, callback) { + var payload = self._state.incoming.payload; + var info; + var cols; + var rows; + var width; + var height; + var wantReply; + var signal; + + var recipient = readInt(payload, 1, self, callback); + if (recipient === false) + return false; + var request = readString(payload, 5, 'ascii', self, callback); + if (request === false) + return false; + + if (request === 'exit-status') { // Server->Client + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "exit-status" + boolean FALSE + uint32 exit_status + */ + var code = readInt(payload, ++payload._pos, self, callback); + if (code === false) + return false; + info = { + recipient: recipient, + request: request, + wantReply: false, + code: code + }; + } else if (request === 'exit-signal') { // Server->Client + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "exit-signal" + boolean FALSE + string signal name (without the "SIG" prefix) + boolean core dumped + string error message in ISO-10646 UTF-8 encoding + string language tag + */ + var coredump; + if (!(self.remoteBugs & BUGS.OLD_EXIT)) { + signal = readString(payload, ++payload._pos, 'ascii', self, callback); + if (signal === false) + return false; + coredump = payload[payload._pos++]; + if (coredump === undefined) + return false; + coredump = (coredump !== 0); + } else { + /* + Instead of `signal name` and `core dumped`, we have just: + + uint32 signal number + */ + signal = readInt(payload, ++payload._pos, self, callback); + if (signal === false) + return false; + switch (signal) { + case 1: + signal = 'HUP'; + break; + case 2: + signal = 'INT'; + break; + case 3: + signal = 'QUIT'; + break; + case 6: + signal = 'ABRT'; + break; + case 9: + signal = 'KILL'; + break; + case 14: + signal = 'ALRM'; + break; + case 15: + signal = 'TERM'; + break; + default: + // Unknown or OS-specific + signal = 'UNKNOWN (' + signal + ')'; + } + coredump = false; + } + var description = readString(payload, payload._pos, 'utf8', self, + callback); + if (description === false) + return false; + var lang = readString(payload, payload._pos, 'utf8', self, callback); + if (lang === false) + return false; + info = { + recipient: recipient, + request: request, + wantReply: false, + signal: signal, + coredump: coredump, + description: description, + lang: lang + }; + } else if (request === 'pty-req') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "pty-req" + boolean want_reply + string TERM environment variable value (e.g., vt100) + uint32 terminal width, characters (e.g., 80) + uint32 terminal height, rows (e.g., 24) + uint32 terminal width, pixels (e.g., 640) + uint32 terminal height, pixels (e.g., 480) + string encoded terminal modes + */ + wantReply = payload[payload._pos++]; + if (wantReply === undefined) + return false; + wantReply = (wantReply !== 0); + var term = readString(payload, payload._pos, 'ascii', self, callback); + if (term === false) + return false; + cols = readInt(payload, payload._pos, self, callback); + if (cols === false) + return false; + rows = readInt(payload, payload._pos += 4, self, callback); + if (rows === false) + return false; + width = readInt(payload, payload._pos += 4, self, callback); + if (width === false) + return false; + height = readInt(payload, payload._pos += 4, self, callback); + if (height === false) + return false; + var modes = readString(payload, payload._pos += 4, self, callback); + if (modes === false) + return false; + modes = bytesToModes(modes); + info = { + recipient: recipient, + request: request, + wantReply: wantReply, + term: term, + cols: cols, + rows: rows, + width: width, + height: height, + modes: modes + }; + } else if (request === 'window-change') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "window-change" + boolean FALSE + uint32 terminal width, columns + uint32 terminal height, rows + uint32 terminal width, pixels + uint32 terminal height, pixels + */ + cols = readInt(payload, ++payload._pos, self, callback); + if (cols === false) + return false; + rows = readInt(payload, payload._pos += 4, self, callback); + if (rows === false) + return false; + width = readInt(payload, payload._pos += 4, self, callback); + if (width === false) + return false; + height = readInt(payload, payload._pos += 4, self, callback); + if (height === false) + return false; + info = { + recipient: recipient, + request: request, + wantReply: false, + cols: cols, + rows: rows, + width: width, + height: height + }; + } else if (request === 'x11-req') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "x11-req" + boolean want reply + boolean single connection + string x11 authentication protocol + string x11 authentication cookie + uint32 x11 screen number + */ + wantReply = payload[payload._pos++]; + if (wantReply === undefined) + return false; + wantReply = (wantReply !== 0); + var single = payload[payload._pos++]; + if (single === undefined) + return false; + single = (single !== 0); + var protocol = readString(payload, payload._pos, 'ascii', self, callback); + if (protocol === false) + return false; + var cookie = readString(payload, payload._pos, 'binary', self, callback); + if (cookie === false) + return false; + var screen = readInt(payload, payload._pos, self, callback); + if (screen === false) + return false; + info = { + recipient: recipient, + request: request, + wantReply: wantReply, + single: single, + protocol: protocol, + cookie: cookie, + screen: screen + }; + } else if (request === 'env') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "env" + boolean want reply + string variable name + string variable value + */ + wantReply = payload[payload._pos++]; + if (wantReply === undefined) + return false; + wantReply = (wantReply !== 0); + var key = readString(payload, payload._pos, 'utf8', self, callback); + if (key === false) + return false; + var val = readString(payload, payload._pos, 'utf8', self, callback); + if (val === false) + return false; + info = { + recipient: recipient, + request: request, + wantReply: wantReply, + key: key, + val: val + }; + } else if (request === 'shell') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "shell" + boolean want reply + */ + wantReply = payload[payload._pos]; + if (wantReply === undefined) + return false; + wantReply = (wantReply !== 0); + info = { + recipient: recipient, + request: request, + wantReply: wantReply + }; + } else if (request === 'exec') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "exec" + boolean want reply + string command + */ + wantReply = payload[payload._pos++]; + if (wantReply === undefined) + return false; + wantReply = (wantReply !== 0); + var command = readString(payload, payload._pos, 'utf8', self, callback); + if (command === false) + return false; + info = { + recipient: recipient, + request: request, + wantReply: wantReply, + command: command + }; + } else if (request === 'subsystem') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "subsystem" + boolean want reply + string subsystem name + */ + wantReply = payload[payload._pos++]; + if (wantReply === undefined) + return false; + wantReply = (wantReply !== 0); + var subsystem = readString(payload, payload._pos, 'utf8', self, callback); + if (subsystem === false) + return false; + info = { + recipient: recipient, + request: request, + wantReply: wantReply, + subsystem: subsystem + }; + } else if (request === 'signal') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "signal" + boolean FALSE + string signal name (without the "SIG" prefix) + */ + signal = readString(payload, ++payload._pos, 'ascii', self, callback); + if (signal === false) + return false; + info = { + recipient: recipient, + request: request, + wantReply: false, + signal: 'SIG' + signal + }; + } else if (request === 'xon-xoff') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "xon-xoff" + boolean FALSE + boolean client can do + */ + var clientControl = payload[++payload._pos]; + if (clientControl === undefined) + return false; + clientControl = (clientControl !== 0); + info = { + recipient: recipient, + request: request, + wantReply: false, + clientControl: clientControl + }; + } else if (request === 'auth-agent-req@openssh.com') { // Client->Server + /* + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "auth-agent-req@openssh.com" + boolean want reply + */ + wantReply = payload[payload._pos]; + if (wantReply === undefined) + return false; + wantReply = (wantReply !== 0); + info = { + recipient: recipient, + request: request, + wantReply: wantReply + }; + } else { + // Unknown request type + wantReply = payload[payload._pos]; + if (wantReply === undefined) + return false; + wantReply = (wantReply !== 0); + info = { + recipient: recipient, + request: request, + wantReply: wantReply + }; + } + self.debug('DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_REQUEST (' + + recipient + + ', ' + + request + + ')'); + self.emit('CHANNEL_REQUEST:' + recipient, info); +} + +function hmacVerify(self, data) { + var instate = self._state.incoming; + var hmac = instate.hmac; + + self.debug('DEBUG: Parser: Verifying MAC'); + + if (instate.decrypt.info.authLen > 0) { + var decrypt = instate.decrypt; + var instance = decrypt.instance; + + instance.setAuthTag(data); + + var payload = instance.update(instate.packet); + instate.payload = payload.slice(1, instate.packet.length - payload[0]); + iv_inc(decrypt.iv); + + decrypt.instance = crypto.createDecipheriv( + SSH_TO_OPENSSL[decrypt.type], + decrypt.key, + decrypt.iv + ); + decrypt.instance.setAutoPadding(false); + return true; + } else { + var calcHmac = crypto.createHmac(SSH_TO_OPENSSL[hmac.type], hmac.key); + + writeUInt32BE(HMAC_COMPUTE, instate.seqno, 0); + writeUInt32BE(HMAC_COMPUTE, instate.pktLen, 4); + HMAC_COMPUTE[8] = instate.padLen; + + calcHmac.update(HMAC_COMPUTE); + calcHmac.update(instate.packet); + + var mac = calcHmac.digest(); + if (mac.length > instate.hmac.info.actualLen) + mac = mac.slice(0, instate.hmac.info.actualLen); + return timingSafeEqual(mac, data); + } +} + +function decryptData(self, data) { + var instance = self._state.incoming.decrypt.instance; + self.debug('DEBUG: Parser: Decrypting'); + return instance.update(data); +} + +function expectData(self, type, amount, buffer) { + var expect = self._state.incoming.expect; + expect.amount = amount; + expect.type = type; + expect.ptr = 0; + if (buffer) + expect.buf = buffer; + else if (amount) + expect.buf = Buffer.allocUnsafe(amount); +} + +function readList(buffer, start, stream, callback) { + var list = readString(buffer, start, 'ascii', stream, callback); + return (list !== false ? (list.length ? list.split(',') : []) : false); +} + +function bytesToModes(buffer) { + var modes = {}; + + for (var i = 0, len = buffer.length, opcode; i < len; i += 5) { + opcode = buffer[i]; + if (opcode === TERMINAL_MODE.TTY_OP_END + || TERMINAL_MODE[opcode] === undefined + || i + 5 > len) + break; + modes[TERMINAL_MODE[opcode]] = readUInt32BE(buffer, i + 1); + } + + return modes; +} + +function modesToBytes(modes) { + var RE_IS_NUM = /^\d+$/; + var keys = Object.keys(modes); + var b = 0; + var bytes = []; + + for (var i = 0, len = keys.length, key, opcode, val; i < len; ++i) { + key = keys[i]; + opcode = TERMINAL_MODE[key]; + if (opcode + && !RE_IS_NUM.test(key) + && typeof modes[key] === 'number' + && key !== 'TTY_OP_END') { + val = modes[key]; + bytes[b++] = opcode; + bytes[b++] = (val >>> 24) & 0xFF; + bytes[b++] = (val >>> 16) & 0xFF; + bytes[b++] = (val >>> 8) & 0xFF; + bytes[b++] = val & 0xFF; + } + } + + bytes[b] = TERMINAL_MODE.TTY_OP_END; + + return bytes; +} + +// Shared outgoing functions +function KEXINIT(self, cb) { // Client/Server + randBytes(16, function(myCookie) { + /* + byte SSH_MSG_KEXINIT + byte[16] cookie (random bytes) + name-list kex_algorithms + name-list server_host_key_algorithms + name-list encryption_algorithms_client_to_server + name-list encryption_algorithms_server_to_client + name-list mac_algorithms_client_to_server + name-list mac_algorithms_server_to_client + name-list compression_algorithms_client_to_server + name-list compression_algorithms_server_to_client + name-list languages_client_to_server + name-list languages_server_to_client + boolean first_kex_packet_follows + uint32 0 (reserved for future extension) + */ + var algos = self.config.algorithms; + + var kexBuf = algos.kexBuf; + if (self.remoteBugs & BUGS.BAD_DHGEX) { + var copied = false; + var kexList = algos.kex; + for (var j = kexList.length - 1; j >= 0; --j) { + if (kexList[j].indexOf('group-exchange') !== -1) { + if (!copied) { + kexList = kexList.slice(); + copied = true; + } + kexList.splice(j, 1); + } + } + if (copied) + kexBuf = Buffer.from(kexList.join(',')); + } + + var hostKeyBuf = algos.serverHostKeyBuf; + + var kexInitSize = 1 + 16 + + 4 + kexBuf.length + + 4 + hostKeyBuf.length + + (2 * (4 + algos.cipherBuf.length)) + + (2 * (4 + algos.hmacBuf.length)) + + (2 * (4 + algos.compressBuf.length)) + + (2 * (4 /* languages skipped */)) + + 1 + 4; + var buf = Buffer.allocUnsafe(kexInitSize); + var p = 17; + + buf[0] = MESSAGE.KEXINIT; + + if (myCookie !== false) + myCookie.copy(buf, 1); + + writeUInt32BE(buf, kexBuf.length, p); + p += 4; + kexBuf.copy(buf, p); + p += kexBuf.length; + + writeUInt32BE(buf, hostKeyBuf.length, p); + p += 4; + hostKeyBuf.copy(buf, p); + p += hostKeyBuf.length; + + writeUInt32BE(buf, algos.cipherBuf.length, p); + p += 4; + algos.cipherBuf.copy(buf, p); + p += algos.cipherBuf.length; + + writeUInt32BE(buf, algos.cipherBuf.length, p); + p += 4; + algos.cipherBuf.copy(buf, p); + p += algos.cipherBuf.length; + + writeUInt32BE(buf, algos.hmacBuf.length, p); + p += 4; + algos.hmacBuf.copy(buf, p); + p += algos.hmacBuf.length; + + writeUInt32BE(buf, algos.hmacBuf.length, p); + p += 4; + algos.hmacBuf.copy(buf, p); + p += algos.hmacBuf.length; + + writeUInt32BE(buf, algos.compressBuf.length, p); + p += 4; + algos.compressBuf.copy(buf, p); + p += algos.compressBuf.length; + + writeUInt32BE(buf, algos.compressBuf.length, p); + p += 4; + algos.compressBuf.copy(buf, p); + p += algos.compressBuf.length; + + // Skip language lists, first_kex_packet_follows, and reserved bytes + buf.fill(0, buf.length - 13); + + self.debug('DEBUG: Outgoing: Writing KEXINIT'); + + self._state.incoming.expectedPacket = 'KEXINIT'; + + var outstate = self._state.outgoing; + + outstate.kexinit = buf; + + if (outstate.status === OUT_READY) { + // We are the one starting the rekeying process ... + outstate.status = OUT_REKEYING; + } + + send(self, buf, cb, true); + }); + return true; +} + +function KEXDH_INIT(self) { // Client + var state = self._state; + var outstate = state.outgoing; + var buf = Buffer.allocUnsafe(1 + 4 + outstate.pubkey.length); + + state.incoming.expectedPacket = state.kex.pktReply; + if (state.kex.type === 'groupex') { + buf[0] = MESSAGE.KEXDH_GEX_INIT; + self.debug('DEBUG: Outgoing: Writing KEXDH_GEX_INIT'); + } else { + buf[0] = MESSAGE.KEXDH_INIT; + if (state.kex.type === 'group') + self.debug('DEBUG: Outgoing: Writing KEXDH_INIT'); + else + self.debug('DEBUG: Outgoing: Writing KEXECDH_INIT'); + } + + writeUInt32BE(buf, outstate.pubkey.length, 1); + outstate.pubkey.copy(buf, 5); + + return send(self, buf, undefined, true); +} + +function KEXDH_REPLY(self, e) { // Server + var state = self._state; + var outstate = state.outgoing; + var instate = state.incoming; + var curHostKey = self.config.hostKeys[state.hostkeyFormat]; + if (Array.isArray(curHostKey)) + curHostKey = curHostKey[0]; + var hostkey = curHostKey.getPublicSSH(); + var hostkeyAlgo = curHostKey.type; + + // e === client DH public key + + e = state.kex.convertPublicKey(e); + + var secret = state.kex.computeSecret(e); + + if (secret instanceof Error) { + secret.message = 'Error while computing DH secret (' + + state.kex.type + '): ' + + secret.message; + secret.level = 'handshake'; + self.emit('error', secret); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + var hash = crypto.createHash(state.kex.hash); + + var len_ident = Buffer.byteLength(instate.identRaw); + var len_sident = Buffer.byteLength(self.config.ident); + var len_init = instate.kexinit.length; + var len_sinit = outstate.kexinit.length; + var len_hostkey = hostkey.length; + var len_pubkey = e.length; + var len_spubkey = outstate.pubkey.length; + var len_secret = secret.length; + + var exchangeBufLen = len_ident + + len_sident + + len_init + + len_sinit + + len_hostkey + + len_pubkey + + len_spubkey + + len_secret + + (4 * 8); // Length fields for above values + + // Group exchange-related + var len_gex_prime; + var len_gex_gen; + var gex_prime; + var gex_gen; + var dhParams = state.kex.getDHParams(); + if (dhParams) { + gex_prime = dhParams.prime; + gex_gen = dhParams.generator; + len_gex_prime = gex_prime.length; + len_gex_gen = gex_gen.length; + exchangeBufLen += (4 * 3); // min, n, max values + exchangeBufLen += (4 * 2); // prime, generator length fields + exchangeBufLen += len_gex_prime; + exchangeBufLen += len_gex_gen; + } + + var bp = 0; + var exchangeBuf = Buffer.allocUnsafe(exchangeBufLen); + + writeUInt32BE(exchangeBuf, len_ident, bp); + bp += 4; + exchangeBuf.write(instate.identRaw, bp, 'utf8'); // V_C + bp += len_ident; + + writeUInt32BE(exchangeBuf, len_sident, bp); + bp += 4; + exchangeBuf.write(self.config.ident, bp, 'utf8'); // V_S + bp += len_sident; + + writeUInt32BE(exchangeBuf, len_init, bp); + bp += 4; + instate.kexinit.copy(exchangeBuf, bp); // I_C + bp += len_init; + instate.kexinit = undefined; + + writeUInt32BE(exchangeBuf, len_sinit, bp); + bp += 4; + outstate.kexinit.copy(exchangeBuf, bp); // I_S + bp += len_sinit; + outstate.kexinit = undefined; + + writeUInt32BE(exchangeBuf, len_hostkey, bp); + bp += 4; + hostkey.copy(exchangeBuf, bp); // K_S + bp += len_hostkey; + + if (dhParams) { + KEXDH_GEX_REQ_PACKET.slice(1).copy(exchangeBuf, bp); // min, n, max + bp += (4 * 3); // Skip over bytes just copied + + writeUInt32BE(exchangeBuf, len_gex_prime, bp); + bp += 4; + gex_prime.copy(exchangeBuf, bp); // p + bp += len_gex_prime; + + writeUInt32BE(exchangeBuf, len_gex_gen, bp); + bp += 4; + gex_gen.copy(exchangeBuf, bp); // g + bp += len_gex_gen; + } + + writeUInt32BE(exchangeBuf, len_pubkey, bp); + bp += 4; + e.copy(exchangeBuf, bp); // e + bp += len_pubkey; + + writeUInt32BE(exchangeBuf, len_spubkey, bp); + bp += 4; + outstate.pubkey.copy(exchangeBuf, bp); // f + bp += len_spubkey; + + writeUInt32BE(exchangeBuf, len_secret, bp); + bp += 4; + secret.copy(exchangeBuf, bp); // K + + outstate.exchangeHash = hash.update(exchangeBuf).digest(); // H + + if (outstate.sessionId === undefined) + outstate.sessionId = outstate.exchangeHash; + outstate.kexsecret = secret; + + var signature = curHostKey.sign(outstate.exchangeHash); + if (signature instanceof Error) { + signature.message = 'Error while signing data with host key (' + + hostkeyAlgo + '): ' + + signature.message; + signature.level = 'handshake'; + self.emit('error', signature); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + signature = convertSignature(signature, hostkeyAlgo); + if (signature === false) { + signature.message = 'Error while converting handshake signature'; + signature.level = 'handshake'; + self.emit('error', signature); + self.disconnect(DISCONNECT_REASON.KEY_EXCHANGE_FAILED); + return false; + } + + /* + byte SSH_MSG_KEXDH_REPLY + string server public host key and certificates (K_S) + mpint f + string signature of H + */ + + var siglen = 4 + hostkeyAlgo.length + 4 + signature.length; + var buf = Buffer.allocUnsafe(1 + + 4 + len_hostkey + + 4 + len_spubkey + + 4 + siglen); + + bp = 0; + buf[bp] = MESSAGE[state.kex.pktReply]; + ++bp; + + writeUInt32BE(buf, len_hostkey, bp); + bp += 4; + hostkey.copy(buf, bp); // K_S + bp += len_hostkey; + + writeUInt32BE(buf, len_spubkey, bp); + bp += 4; + outstate.pubkey.copy(buf, bp); // f + bp += len_spubkey; + + writeUInt32BE(buf, siglen, bp); + bp += 4; + writeUInt32BE(buf, hostkeyAlgo.length, bp); + bp += 4; + buf.write(hostkeyAlgo, bp, hostkeyAlgo.length, 'ascii'); + bp += hostkeyAlgo.length; + writeUInt32BE(buf, signature.length, bp); + bp += 4; + signature.copy(buf, bp); + + state.incoming.expectedPacket = 'NEWKEYS'; + + self.debug('DEBUG: Outgoing: Writing ' + state.kex.pktReply); + send(self, buf, undefined, true); + + outstate.sentNEWKEYS = true; + self.debug('DEBUG: Outgoing: Writing NEWKEYS'); + return send(self, NEWKEYS_PACKET, undefined, true); +} + +function KEXDH_GEX_REQ(self) { // Client + self._state.incoming.expectedPacket = 'KEXDH_GEX_GROUP'; + + self.debug('DEBUG: Outgoing: Writing KEXDH_GEX_REQUEST'); + return send(self, KEXDH_GEX_REQ_PACKET, undefined, true); +} + +function compressPayload(self, payload, cb) { + var compress = self._state.outgoing.compress.instance; + compress.write(payload); + compress.flush(Z_PARTIAL_FLUSH, compressFlushCb.bind(self, cb)); +} + +function compressFlushCb(cb) { + if (this._readableState.ended || this._writableState.ended) + return; + send_(this, this._state.outgoing.compress.instance.read(), cb); + + var queue = this._state.outgoing.compress.queue; + queue.shift(); + if (queue.length > 0) + compressPayload(this, queue[0][0], queue[0][1]); +} + +function send(self, payload, cb, bypass) { + var state = self._state; + + if (!state) + return false; + + var outstate = state.outgoing; + if (outstate.status === OUT_REKEYING && !bypass) { + if (typeof cb === 'function') + outstate.rekeyQueue.push([payload, cb]); + else + outstate.rekeyQueue.push(payload); + return false; + } else if (self._readableState.ended || self._writableState.ended) { + return false; + } + + if (outstate.compress.instance) { + // This queue nonsense only exists because of a change made in node v10.12.0 + // that changed flushing behavior, which now coalesces multiple writes to a + // single flush, which does not work for us. + var queue = outstate.compress.queue; + queue.push([payload, cb]); + if (queue.length === 1) + compressPayload(self, queue[0][0], queue[0][1]); + return true; + } else { + return send_(self, payload, cb); + } +} + +function send_(self, payload, cb) { + // TODO: Implement length checks + + var state = self._state; + var outstate = state.outgoing; + var encrypt = outstate.encrypt; + var hmac = outstate.hmac; + var pktLen; + var padLen; + var buf; + var mac; + var ret; + + pktLen = payload.length + 9; + + if (encrypt.instance !== false) { + if (encrypt.info.authLen > 0) { + var ptlen = 1 + payload.length + 4/* Must have at least 4 bytes padding*/; + while ((ptlen % encrypt.info.blockLen) !== 0) + ++ptlen; + padLen = ptlen - 1 - payload.length; + pktLen = 4 + ptlen; + } else { + var blockLen = encrypt.info.blockLen; + pktLen += ((blockLen - 1) * pktLen) % blockLen; + padLen = pktLen - payload.length - 5; + } + } else { + pktLen += (7 * pktLen) % 8; + padLen = pktLen - payload.length - 5; + } + + buf = Buffer.allocUnsafe(pktLen); + + writeUInt32BE(buf, pktLen - 4, 0); + buf[4] = padLen; + payload.copy(buf, 5); + + copyRandPadBytes(buf, 5 + payload.length, padLen); + + if (hmac.type !== false && hmac.key) { + mac = crypto.createHmac(SSH_TO_OPENSSL[hmac.type], hmac.key); + writeUInt32BE(outstate.bufSeqno, outstate.seqno, 0); + mac.update(outstate.bufSeqno); + mac.update(buf); + mac = mac.digest(); + if (mac.length > hmac.info.actualLen) + mac = mac.slice(0, hmac.info.actualLen); + } + + var nb = 0; + var encData; + + if (encrypt.instance !== false) { + if (encrypt.info.authLen > 0) { + var encrypter = crypto.createCipheriv(SSH_TO_OPENSSL[encrypt.type], + encrypt.key, + encrypt.iv); + encrypter.setAutoPadding(false); + + var lenbuf = buf.slice(0, 4); + + encrypter.setAAD(lenbuf); + self.push(lenbuf); + nb += lenbuf; + + encData = encrypter.update(buf.slice(4)); + self.push(encData); + nb += encData.length; + + var final = encrypter.final(); + if (final.length) { + self.push(final); + nb += final.length; + } + + var authTag = encrypter.getAuthTag(); + ret = self.push(authTag); + nb += authTag.length; + + iv_inc(encrypt.iv); + } else { + encData = encrypt.instance.update(buf); + self.push(encData); + nb += encData.length; + + ret = self.push(mac); + nb += mac.length; + } + } else { + ret = self.push(buf); + nb = buf.length; + } + + self.bytesSent += nb; + + if (++outstate.seqno > MAX_SEQNO) + outstate.seqno = 0; + + cb && cb(); + + return ret; +} + +var copyRandPadBytes = (function() { + if (typeof crypto.randomFillSync === 'function') { + return crypto.randomFillSync; + } else { + return function copyRandPadBytes(buf, offset, count) { + var padBytes = crypto.randomBytes(count); + padBytes.copy(buf, offset); + }; + } +})(); + +function randBytes(n, cb) { + crypto.randomBytes(n, function retry(err, buf) { + if (err) + return crypto.randomBytes(n, retry); + cb && cb(buf); + }); +} + +function convertSignature(signature, keyType) { + switch (keyType) { + case 'ssh-dss': + return DSASigBERToBare(signature); + case 'ecdsa-sha2-nistp256': + case 'ecdsa-sha2-nistp384': + case 'ecdsa-sha2-nistp521': + return ECDSASigASN1ToSSH(signature); + } + + return signature; +} + +var timingSafeEqual = (function() { + if (typeof crypto.timingSafeEqual === 'function') { + return function timingSafeEquals(a, b) { + if (a.length !== b.length) { + crypto.timingSafeEqual(a, a); + return false; + } else { + return crypto.timingSafeEqual(a, b); + } + }; + } else { + return function timingSafeEquals(a, b) { + var val; + if (a.length === b.length) { + val = 0; + } else { + val = 1; + b = a; + } + + for (var i = 0, len = a.length; i < len; ++i) + val |= (a[i] ^ b[i]); + + return (val === 0); + } + } +})(); + +function KeyExchange(algo, options) { + switch (algo) { + case 'curve25519-sha256': + case 'curve25519-sha256@libssh.org': + if (!CURVE25519_SUPPORTED) + break; + this.type = '25519'; + this.hash = 'sha256'; + this.pktInit = 'KEXECDH_INIT'; + this.pktReply = 'KEXECDH_REPLY'; + return; + case 'ecdh-sha2-nistp256': + this.type = 'ecdh'; + this.name = 'prime256v1'; + this.hash = 'sha256'; + this.pktInit = 'KEXECDH_INIT'; + this.pktReply = 'KEXECDH_REPLY'; + return; + case 'ecdh-sha2-nistp384': + this.type = 'ecdh'; + this.name = 'secp384r1'; + this.hash = 'sha384'; + this.pktInit = 'KEXECDH_INIT'; + this.pktReply = 'KEXECDH_REPLY'; + return; + case 'ecdh-sha2-nistp521': + this.type = 'ecdh'; + this.name = 'secp521r1'; + this.hash = 'sha512'; + this.pktInit = 'KEXECDH_INIT'; + this.pktReply = 'KEXECDH_REPLY'; + return; + case 'diffie-hellman-group1-sha1': + this.type = 'group'; + this.name = 'modp2'; + this.hash = 'sha1'; + this.pktInit = 'KEXDH_INIT'; + this.pktReply = 'KEXDH_REPLY'; + return; + case 'diffie-hellman-group14-sha1': + this.type = 'group'; + this.name = 'modp14'; + this.hash = 'sha1'; + this.pktInit = 'KEXDH_INIT'; + this.pktReply = 'KEXDH_REPLY'; + return; + case 'diffie-hellman-group14-sha256': + this.type = 'group'; + this.name = 'modp14'; + this.hash = 'sha256'; + this.pktInit = 'KEXDH_INIT'; + this.pktReply = 'KEXDH_REPLY'; + return; + case 'diffie-hellman-group16-sha512': + this.type = 'group'; + this.name = 'modp16'; + this.hash = 'sha512'; + this.pktInit = 'KEXDH_INIT'; + this.pktReply = 'KEXDH_REPLY'; + return; + case 'diffie-hellman-group18-sha512': + this.type = 'group'; + this.name = 'modp18'; + this.hash = 'sha512'; + this.pktInit = 'KEXDH_INIT'; + this.pktReply = 'KEXDH_REPLY'; + return; + case 'diffie-hellman-group-exchange-sha1': + this.type = 'groupex'; + this.hash = 'sha1'; + this.pktInit = 'KEXDH_GEX_REQ'; + this.pktReply = 'KEXDH_GEX_REPLY'; + this._prime = null; + this._generator = null; + return; + case 'diffie-hellman-group-exchange-sha256': + this.type = 'groupex'; + this.hash = 'sha256'; + this.pktInit = 'KEXDH_GEX_REQ'; + this.pktReply = 'KEXDH_GEX_REPLY'; + this._prime = null; + this._generator = null; + return; + } + throw new Error('Unsupported key exchange algorithm: ' + algo); +} +KeyExchange.prototype.setDHParams = function(prime, generator) { + if (this.type === 'groupex') { + if (!Buffer.isBuffer(prime)) + throw new Error('Invalid prime value'); + if (!Buffer.isBuffer(generator)) + throw new Error('Invalid generator value'); + this._prime = prime; + this._generator = generator; + } +}; +KeyExchange.prototype.getDHParams = function() { + if (this.type === 'groupex' && this._kex) { + return { + prime: convertToMpint(this._kex.getPrime()), + generator: convertToMpint(this._kex.getGenerator()), + }; + } +}; +KeyExchange.prototype.generateKeys = function() { + switch (this.type) { + case '25519': + if (!this._keys) + this._keys = crypto.generateKeyPairSync('x25519'); + break; + case 'ecdh': + if (!this._kex) { + this._kex = crypto.createECDH(this.name); + this._public = this._kex.generateKeys(); + } + break; + case 'group': + case 'groupex': + if (!this._kex) { + if (this.name) + this._kex = crypto.createDiffieHellmanGroup(this.name); + else if (this._prime && this._generator) + this._kex = crypto.createDiffieHellman(this._prime, this._generator); + if (this._kex) + this._public = this._kex.generateKeys(); + } + break; + } +}; +KeyExchange.prototype.getPublicKey = function() { + this.generateKeys(); + + var key; + switch (this.type) { + case '25519': + key = this._keys.publicKey.export({ type: 'spki', format: 'der' }); + return key.slice(-32); // HACK: avoids parsing DER/BER header + case 'ecdh': + case 'group': + case 'groupex': + key = this._public; + break; + } + if (key) + return this.convertPublicKey(key); +}; +KeyExchange.prototype.convertPublicKey = function(key) { + var newKey; + var idx = 0; + var len = key.length; + while (key[idx] === 0x00) { + ++idx; + --len; + } + switch (this.type) { + case '25519': + if (key.length === 32) + return key; + break; + default: + if (key[idx] & 0x80) { + newKey = Buffer.allocUnsafe(1 + len); + newKey[0] = 0; + key.copy(newKey, 1, idx); + return newKey; + } + } + if (len !== key.length) { + newKey = Buffer.allocUnsafe(len); + key.copy(newKey, 0, idx); + key = newKey; + } + return key; +}; +KeyExchange.prototype.computeSecret = function(otherPublicKey) { + this.generateKeys(); + + switch (this.type) { + case '25519': + try { + var asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + // algorithm + asnWriter.startSequence(); + asnWriter.writeOID('1.3.101.110'); // id-X25519 + asnWriter.endSequence(); + + // PublicKey + asnWriter.startSequence(Ber.BitString); + asnWriter.writeByte(0x00); + // XXX: hack to write a raw buffer without a tag -- yuck + asnWriter._ensure(otherPublicKey.length); + otherPublicKey.copy(asnWriter._buf, + asnWriter._offset, + 0, + otherPublicKey.length); + asnWriter._offset += otherPublicKey.length; + asnWriter.endSequence(); + asnWriter.endSequence(); + + return convertToMpint(crypto.diffieHellman({ + privateKey: this._keys.privateKey, + publicKey: crypto.createPublicKey({ + key: asnWriter.buffer, + type: 'spki', + format: 'der', + }), + })); + } catch (ex) { + return ex; + } + break; + case 'ecdh': + case 'group': + case 'groupex': + try { + return convertToMpint(this._kex.computeSecret(otherPublicKey)); + } catch (ex) { + return ex; + } + } +}; + +function convertToMpint(buf) { + var idx = 0; + var length = buf.length; + while (buf[idx] === 0x00) { + ++idx; + --length; + } + var newBuf; + if (buf[idx] & 0x80) { + newBuf = Buffer.allocUnsafe(1 + length); + newBuf[0] = 0; + buf.copy(newBuf, 1, idx); + buf = newBuf; + } else if (length !== buf.length) { + newBuf = Buffer.allocUnsafe(length); + buf.copy(newBuf, 0, idx); + buf = newBuf; + } + return buf; +} + +module.exports = SSH2Stream; +module.exports._send = send; + + +/***/ }), + +/***/ 641: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var crypto_hash_sha512 = __webpack_require__(196).lowlevel.crypto_hash; + +/* + * This file is a 1:1 port from the OpenBSD blowfish.c and bcrypt_pbkdf.c. As a + * result, it retains the original copyright and license. The two files are + * under slightly different (but compatible) licenses, and are here combined in + * one file. + * + * Credit for the actual porting work goes to: + * Devi Mandiri + */ + +/* + * The Blowfish portions are under the following license: + * + * Blowfish block cipher for OpenBSD + * Copyright 1997 Niels Provos + * All rights reserved. + * + * Implementation advice by David Mazieres . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The bcrypt_pbkdf portions are under the following license: + * + * Copyright (c) 2013 Ted Unangst + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Performance improvements (Javascript-specific): + * + * Copyright 2016, Joyent Inc + * Author: Alex Wilson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// Ported from OpenBSD bcrypt_pbkdf.c v1.9 + +var BLF_J = 0; + +var Blowfish = function() { + this.S = [ + new Uint32Array([ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a]), + new Uint32Array([ + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7]), + new Uint32Array([ + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0]), + new Uint32Array([ + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6]) + ]; + this.P = new Uint32Array([ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b]); +}; + +function F(S, x8, i) { + return (((S[0][x8[i+3]] + + S[1][x8[i+2]]) ^ + S[2][x8[i+1]]) + + S[3][x8[i]]); +}; + +Blowfish.prototype.encipher = function(x, x8) { + if (x8 === undefined) { + x8 = new Uint8Array(x.buffer); + if (x.byteOffset !== 0) + x8 = x8.subarray(x.byteOffset); + } + x[0] ^= this.P[0]; + for (var i = 1; i < 16; i += 2) { + x[1] ^= F(this.S, x8, 0) ^ this.P[i]; + x[0] ^= F(this.S, x8, 4) ^ this.P[i+1]; + } + var t = x[0]; + x[0] = x[1] ^ this.P[17]; + x[1] = t; +}; + +Blowfish.prototype.decipher = function(x) { + var x8 = new Uint8Array(x.buffer); + if (x.byteOffset !== 0) + x8 = x8.subarray(x.byteOffset); + x[0] ^= this.P[17]; + for (var i = 16; i > 0; i -= 2) { + x[1] ^= F(this.S, x8, 0) ^ this.P[i]; + x[0] ^= F(this.S, x8, 4) ^ this.P[i-1]; + } + var t = x[0]; + x[0] = x[1] ^ this.P[0]; + x[1] = t; +}; + +function stream2word(data, databytes){ + var i, temp = 0; + for (i = 0; i < 4; i++, BLF_J++) { + if (BLF_J >= databytes) BLF_J = 0; + temp = (temp << 8) | data[BLF_J]; + } + return temp; +}; + +Blowfish.prototype.expand0state = function(key, keybytes) { + var d = new Uint32Array(2), i, k; + var d8 = new Uint8Array(d.buffer); + + for (i = 0, BLF_J = 0; i < 18; i++) { + this.P[i] ^= stream2word(key, keybytes); + } + BLF_J = 0; + + for (i = 0; i < 18; i += 2) { + this.encipher(d, d8); + this.P[i] = d[0]; + this.P[i+1] = d[1]; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + this.encipher(d, d8); + this.S[i][k] = d[0]; + this.S[i][k+1] = d[1]; + } + } +}; + +Blowfish.prototype.expandstate = function(data, databytes, key, keybytes) { + var d = new Uint32Array(2), i, k; + + for (i = 0, BLF_J = 0; i < 18; i++) { + this.P[i] ^= stream2word(key, keybytes); + } + + for (i = 0, BLF_J = 0; i < 18; i += 2) { + d[0] ^= stream2word(data, databytes); + d[1] ^= stream2word(data, databytes); + this.encipher(d); + this.P[i] = d[0]; + this.P[i+1] = d[1]; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + d[0] ^= stream2word(data, databytes); + d[1] ^= stream2word(data, databytes); + this.encipher(d); + this.S[i][k] = d[0]; + this.S[i][k+1] = d[1]; + } + } + BLF_J = 0; +}; + +Blowfish.prototype.enc = function(data, blocks) { + for (var i = 0; i < blocks; i++) { + this.encipher(data.subarray(i*2)); + } +}; + +Blowfish.prototype.dec = function(data, blocks) { + for (var i = 0; i < blocks; i++) { + this.decipher(data.subarray(i*2)); + } +}; + +var BCRYPT_BLOCKS = 8, + BCRYPT_HASHSIZE = 32; + +function bcrypt_hash(sha2pass, sha2salt, out) { + var state = new Blowfish(), + cdata = new Uint32Array(BCRYPT_BLOCKS), i, + ciphertext = new Uint8Array([79,120,121,99,104,114,111,109,97,116,105, + 99,66,108,111,119,102,105,115,104,83,119,97,116,68,121,110,97,109, + 105,116,101]); //"OxychromaticBlowfishSwatDynamite" + + state.expandstate(sha2salt, 64, sha2pass, 64); + for (i = 0; i < 64; i++) { + state.expand0state(sha2salt, 64); + state.expand0state(sha2pass, 64); + } + + for (i = 0; i < BCRYPT_BLOCKS; i++) + cdata[i] = stream2word(ciphertext, ciphertext.byteLength); + for (i = 0; i < 64; i++) + state.enc(cdata, cdata.byteLength / 8); + + for (i = 0; i < BCRYPT_BLOCKS; i++) { + out[4*i+3] = cdata[i] >>> 24; + out[4*i+2] = cdata[i] >>> 16; + out[4*i+1] = cdata[i] >>> 8; + out[4*i+0] = cdata[i]; + } +}; + +function bcrypt_pbkdf(pass, passlen, salt, saltlen, key, keylen, rounds) { + var sha2pass = new Uint8Array(64), + sha2salt = new Uint8Array(64), + out = new Uint8Array(BCRYPT_HASHSIZE), + tmpout = new Uint8Array(BCRYPT_HASHSIZE), + countsalt = new Uint8Array(saltlen+4), + i, j, amt, stride, dest, count, + origkeylen = keylen; + + if (rounds < 1) + return -1; + if (passlen === 0 || saltlen === 0 || keylen === 0 || + keylen > (out.byteLength * out.byteLength) || saltlen > (1<<20)) + return -1; + + stride = Math.floor((keylen + out.byteLength - 1) / out.byteLength); + amt = Math.floor((keylen + stride - 1) / stride); + + for (i = 0; i < saltlen; i++) + countsalt[i] = salt[i]; + + crypto_hash_sha512(sha2pass, pass, passlen); + + for (count = 1; keylen > 0; count++) { + countsalt[saltlen+0] = count >>> 24; + countsalt[saltlen+1] = count >>> 16; + countsalt[saltlen+2] = count >>> 8; + countsalt[saltlen+3] = count; + + crypto_hash_sha512(sha2salt, countsalt, saltlen + 4); + bcrypt_hash(sha2pass, sha2salt, tmpout); + for (i = out.byteLength; i--;) + out[i] = tmpout[i]; + + for (i = 1; i < rounds; i++) { + crypto_hash_sha512(sha2salt, tmpout, tmpout.byteLength); + bcrypt_hash(sha2pass, sha2salt, tmpout); + for (j = 0; j < out.byteLength; j++) + out[j] ^= tmpout[j]; + } + + amt = Math.min(amt, keylen); + for (i = 0; i < amt; i++) { + dest = i * stride + (count - 1); + if (dest >= origkeylen) + break; + key[dest] = out[i]; + } + keylen -= i; + } + + return 0; +}; + +module.exports = { + BLOCKS: BCRYPT_BLOCKS, + HASHSIZE: BCRYPT_HASHSIZE, + hash: bcrypt_hash, + pbkdf: bcrypt_pbkdf +}; + + +/***/ }), + +/***/ 652: +/***/ (function(module) { + +// Copyright (c) 2005 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Basic JavaScript BN library - subset useful for RSA encryption. + +// Bits per digit +var dbits; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +// (public) Constructor +function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +} + +// return new, unset BigInteger +function nbi() { return new BigInteger(null); } + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// Set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; +} +BigInteger.prototype.am = am3; +dbits = 28; + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this[0] = x; + else if(x < -1) this[0] = x+this.DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + +// (protected) set from string and radix +function bnpFromString(s,b) { + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this[this.t++] = x; + else if(sh+k > this.DB) { + this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); + } + else + this[this.t-1] |= x<= this.DB) sh -= this.DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1< 0) { + if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this[i]&((1<>(p+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return (this.s<0)?-r:r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n,r) { + r.s = this.s; + var ds = Math.floor(n/this.DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<>bs; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); + +// Copyright (c) 2005-2009 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Extended JavaScript BN functions, required for RSA private ops. + +// Version 1.1: new BigInteger("0", 10) returns "proper" zero +// Version 1.2: square() API, isProbablePrime fix + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<>24; } + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 0) { + if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) + r[k++] = d|(this.s<<(this.DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this[i]&((1<>(p+=this.DB-8); + } + else { + d = (this[i]>>(p-=8))&0xff; + if(p <= 0) { p += this.DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); + if(a.t < this.t) { + f = a.s&this.DM; + for(i = m; i < this.t; ++i) r[i] = op(this[i],f); + r.t = this.t; + } + else { + f = this.s&this.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); +} + +// (protected) this op (1<>= this.DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = this.DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this^2 +function bnSquare() { var r = nbi(); this.squareTo(r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + if(n == 0) return; + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j])-1; + while(j >= 0) { + if(i >= k1) w = (e[j]>>(i-k1))&km; + else { + w = (e[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e[j-1]>>(this.DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += this.DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e[j]&(1< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]; +var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + +// (public) test primality with certainty >= 1-.5^t +function bnIsProbablePrime(t) { + var i, x = this.abs(); + if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); +} + +// (protected) true if probably prime (HAC 4.24, Miller-Rabin) +function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + //Pick bases at random, instead of starting at 2 + a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; +} + +// protected +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.fromNumber = bnpFromNumber; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.changeBit = bnpChangeBit; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; +BigInteger.prototype.modInt = bnpModInt; +BigInteger.prototype.millerRabin = bnpMillerRabin; + +// public +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.byteValue = bnByteValue; +BigInteger.prototype.shortValue = bnShortValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.andNot = bnAndNot; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.shiftLeft = bnShiftLeft; +BigInteger.prototype.shiftRight = bnShiftRight; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.bitCount = bnBitCount; +BigInteger.prototype.testBit = bnTestBit; +BigInteger.prototype.setBit = bnSetBit; +BigInteger.prototype.clearBit = bnClearBit; +BigInteger.prototype.flipBit = bnFlipBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.modInverse = bnModInverse; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + +// JSBN-specific extension +BigInteger.prototype.square = bnSquare; + +// Expose the Barrett function +BigInteger.prototype.Barrett = Barrett + +// BigInteger interfaces not implemented in jsbn: + +// BigInteger(int signum, byte[] magnitude) +// double doubleValue() +// float floatValue() +// int hashCode() +// long longValue() +// static BigInteger valueOf(long val) + +module.exports = BigInteger; + + + +/***/ }), + +/***/ 669: +/***/ (function(module) { + +module.exports = require("util"); + +/***/ }), + +/***/ 670: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +const { Buffer } = __webpack_require__(293) +const symbol = Symbol.for('BufferList') + +function BufferList (buf) { + if (!(this instanceof BufferList)) { + return new BufferList(buf) + } + + BufferList._init.call(this, buf) +} + +BufferList._init = function _init (buf) { + Object.defineProperty(this, symbol, { value: true }) + + this._bufs = [] + this.length = 0 + + if (buf) { + this.append(buf) + } +} + +BufferList.prototype._new = function _new (buf) { + return new BufferList(buf) +} + +BufferList.prototype._offset = function _offset (offset) { + if (offset === 0) { + return [0, 0] + } + + let tot = 0 + + for (let i = 0; i < this._bufs.length; i++) { + const _t = tot + this._bufs[i].length + if (offset < _t || i === this._bufs.length - 1) { + return [i, offset - tot] + } + tot = _t + } +} + +BufferList.prototype._reverseOffset = function (blOffset) { + const bufferId = blOffset[0] + let offset = blOffset[1] + + for (let i = 0; i < bufferId; i++) { + offset += this._bufs[i].length + } + + return offset +} + +BufferList.prototype.get = function get (index) { + if (index > this.length || index < 0) { + return undefined + } + + const offset = this._offset(index) + + return this._bufs[offset[0]][offset[1]] +} + +BufferList.prototype.slice = function slice (start, end) { + if (typeof start === 'number' && start < 0) { + start += this.length + } + + if (typeof end === 'number' && end < 0) { + end += this.length + } + + return this.copy(null, 0, start, end) +} + +BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) { + if (typeof srcStart !== 'number' || srcStart < 0) { + srcStart = 0 + } + + if (typeof srcEnd !== 'number' || srcEnd > this.length) { + srcEnd = this.length + } + + if (srcStart >= this.length) { + return dst || Buffer.alloc(0) + } + + if (srcEnd <= 0) { + return dst || Buffer.alloc(0) + } + + const copy = !!dst + const off = this._offset(srcStart) + const len = srcEnd - srcStart + let bytes = len + let bufoff = (copy && dstStart) || 0 + let start = off[1] + + // copy/slice everything + if (srcStart === 0 && srcEnd === this.length) { + if (!copy) { + // slice, but full concat if multiple buffers + return this._bufs.length === 1 + ? this._bufs[0] + : Buffer.concat(this._bufs, this.length) + } + + // copy, need to copy individual buffers + for (let i = 0; i < this._bufs.length; i++) { + this._bufs[i].copy(dst, bufoff) + bufoff += this._bufs[i].length + } + + return dst + } + + // easy, cheap case where it's a subset of one of the buffers + if (bytes <= this._bufs[off[0]].length - start) { + return copy + ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes) + : this._bufs[off[0]].slice(start, start + bytes) + } + + if (!copy) { + // a slice, we need something to copy in to + dst = Buffer.allocUnsafe(len) + } + + for (let i = off[0]; i < this._bufs.length; i++) { + const l = this._bufs[i].length - start + + if (bytes > l) { + this._bufs[i].copy(dst, bufoff, start) + } else { + this._bufs[i].copy(dst, bufoff, start, start + bytes) + break + } + + bufoff += l + bytes -= l + + if (start) { + start = 0 + } + } + + return dst +} + +BufferList.prototype.shallowSlice = function shallowSlice (start, end) { + start = start || 0 + end = typeof end !== 'number' ? this.length : end + + if (start < 0) { + start += this.length + } + + if (end < 0) { + end += this.length + } + + if (start === end) { + return this._new() + } + + const startOffset = this._offset(start) + const endOffset = this._offset(end) + const buffers = this._bufs.slice(startOffset[0], endOffset[0] + 1) + + if (endOffset[1] === 0) { + buffers.pop() + } else { + buffers[buffers.length - 1] = buffers[buffers.length - 1].slice(0, endOffset[1]) + } + + if (startOffset[1] !== 0) { + buffers[0] = buffers[0].slice(startOffset[1]) + } + + return this._new(buffers) +} + +BufferList.prototype.toString = function toString (encoding, start, end) { + return this.slice(start, end).toString(encoding) +} + +BufferList.prototype.consume = function consume (bytes) { + while (this._bufs.length) { + if (bytes >= this._bufs[0].length) { + bytes -= this._bufs[0].length + this.length -= this._bufs[0].length + this._bufs.shift() + } else { + this._bufs[0] = this._bufs[0].slice(bytes) + this.length -= bytes + break + } + } + + return this +} + +BufferList.prototype.duplicate = function duplicate () { + const copy = this._new() + + for (let i = 0; i < this._bufs.length; i++) { + copy.append(this._bufs[i]) + } + + return copy +} + +BufferList.prototype.append = function append (buf) { + if (buf == null) { + return this + } + + if (buf.buffer) { + // append a view of the underlying ArrayBuffer + this._appendBuffer(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)) + } else if (Array.isArray(buf)) { + for (let i = 0; i < buf.length; i++) { + this.append(buf[i]) + } + } else if (this._isBufferList(buf)) { + // unwrap argument into individual BufferLists + for (let i = 0; i < buf._bufs.length; i++) { + this.append(buf._bufs[i]) + } + } else { + // coerce number arguments to strings, since Buffer(number) does + // uninitialized memory allocation + if (typeof buf === 'number') { + buf = buf.toString() + } + + this._appendBuffer(Buffer.from(buf)) + } + + return this +} + +BufferList.prototype._appendBuffer = function appendBuffer (buf) { + this._bufs.push(buf) + this.length += buf.length +} + +BufferList.prototype.indexOf = function (search, offset, encoding) { + if (encoding === undefined && typeof offset === 'string') { + encoding = offset + offset = undefined + } + + if (typeof search === 'function' || Array.isArray(search)) { + throw new TypeError('The "value" argument must be one of type string, Buffer, BufferList, or Uint8Array.') + } else if (typeof search === 'number') { + search = Buffer.from([search]) + } else if (typeof search === 'string') { + search = Buffer.from(search, encoding) + } else if (this._isBufferList(search)) { + search = search.slice() + } else if (Array.isArray(search.buffer)) { + search = Buffer.from(search.buffer, search.byteOffset, search.byteLength) + } else if (!Buffer.isBuffer(search)) { + search = Buffer.from(search) + } + + offset = Number(offset || 0) + + if (isNaN(offset)) { + offset = 0 + } + + if (offset < 0) { + offset = this.length + offset + } + + if (offset < 0) { + offset = 0 + } + + if (search.length === 0) { + return offset > this.length ? this.length : offset + } + + const blOffset = this._offset(offset) + let blIndex = blOffset[0] // index of which internal buffer we're working on + let buffOffset = blOffset[1] // offset of the internal buffer we're working on + + // scan over each buffer + for (; blIndex < this._bufs.length; blIndex++) { + const buff = this._bufs[blIndex] + + while (buffOffset < buff.length) { + const availableWindow = buff.length - buffOffset + + if (availableWindow >= search.length) { + const nativeSearchResult = buff.indexOf(search, buffOffset) + + if (nativeSearchResult !== -1) { + return this._reverseOffset([blIndex, nativeSearchResult]) + } + + buffOffset = buff.length - search.length + 1 // end of native search window + } else { + const revOffset = this._reverseOffset([blIndex, buffOffset]) + + if (this._match(revOffset, search)) { + return revOffset + } + + buffOffset++ + } + } + + buffOffset = 0 + } + + return -1 +} + +BufferList.prototype._match = function (offset, search) { + if (this.length - offset < search.length) { + return false + } + + for (let searchOffset = 0; searchOffset < search.length; searchOffset++) { + if (this.get(offset + searchOffset) !== search[searchOffset]) { + return false + } + } + return true +} + +;(function () { + const methods = { + readDoubleBE: 8, + readDoubleLE: 8, + readFloatBE: 4, + readFloatLE: 4, + readInt32BE: 4, + readInt32LE: 4, + readUInt32BE: 4, + readUInt32LE: 4, + readInt16BE: 2, + readInt16LE: 2, + readUInt16BE: 2, + readUInt16LE: 2, + readInt8: 1, + readUInt8: 1, + readIntBE: null, + readIntLE: null, + readUIntBE: null, + readUIntLE: null + } + + for (const m in methods) { + (function (m) { + if (methods[m] === null) { + BufferList.prototype[m] = function (offset, byteLength) { + return this.slice(offset, offset + byteLength)[m](0, byteLength) + } + } else { + BufferList.prototype[m] = function (offset) { + return this.slice(offset, offset + methods[m])[m](0) + } + } + }(m)) + } +}()) + +// Used internally by the class and also as an indicator of this object being +// a `BufferList`. It's not possible to use `instanceof BufferList` in a browser +// environment because there could be multiple different copies of the +// BufferList class and some `BufferList`s might be `BufferList`s. +BufferList.prototype._isBufferList = function _isBufferList (b) { + return b instanceof BufferList || BufferList.isBufferList(b) +} + +BufferList.isBufferList = function isBufferList (b) { + return b != null && b[symbol] +} + +module.exports = BufferList + + +/***/ }), + +/***/ 674: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +/**/ + +var Buffer = __webpack_require__(149).Buffer; +/**/ + +var isEncoding = Buffer.isEncoding || function (encoding) { + encoding = '' + encoding; + switch (encoding && encoding.toLowerCase()) { + case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': + return true; + default: + return false; + } +}; + +function _normalizeEncoding(enc) { + if (!enc) return 'utf8'; + var retried; + while (true) { + switch (enc) { + case 'utf8': + case 'utf-8': + return 'utf8'; + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return 'utf16le'; + case 'latin1': + case 'binary': + return 'latin1'; + case 'base64': + case 'ascii': + case 'hex': + return enc; + default: + if (retried) return; // undefined + enc = ('' + enc).toLowerCase(); + retried = true; + } + } +}; + +// Do not cache `Buffer.isEncoding` when checking encoding names as some +// modules monkey-patch it to support additional encodings +function normalizeEncoding(enc) { + var nenc = _normalizeEncoding(enc); + if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); + return nenc || enc; +} + +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. +exports.StringDecoder = StringDecoder; +function StringDecoder(encoding) { + this.encoding = normalizeEncoding(encoding); + var nb; + switch (this.encoding) { + case 'utf16le': + this.text = utf16Text; + this.end = utf16End; + nb = 4; + break; + case 'utf8': + this.fillLast = utf8FillLast; + nb = 4; + break; + case 'base64': + this.text = base64Text; + this.end = base64End; + nb = 3; + break; + default: + this.write = simpleWrite; + this.end = simpleEnd; + return; + } + this.lastNeed = 0; + this.lastTotal = 0; + this.lastChar = Buffer.allocUnsafe(nb); +} + +StringDecoder.prototype.write = function (buf) { + if (buf.length === 0) return ''; + var r; + var i; + if (this.lastNeed) { + r = this.fillLast(buf); + if (r === undefined) return ''; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; + } + if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); + return r || ''; +}; + +StringDecoder.prototype.end = utf8End; + +// Returns only complete characters in a Buffer +StringDecoder.prototype.text = utf8Text; + +// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer +StringDecoder.prototype.fillLast = function (buf) { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +}; + +// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a +// continuation byte. If an invalid byte is detected, -2 is returned. +function utf8CheckByte(byte) { + if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; + return byte >> 6 === 0x02 ? -1 : -2; +} + +// Checks at most 3 bytes at the end of a Buffer in order to detect an +// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) +// needed to complete the UTF-8 character (if applicable) are returned. +function utf8CheckIncomplete(self, buf, i) { + var j = buf.length - 1; + if (j < i) return 0; + var nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0;else self.lastNeed = nb - 3; + } + return nb; + } + return 0; +} + +// Validates as many continuation bytes for a multi-byte UTF-8 character as +// needed or are available. If we see a non-continuation byte where we expect +// one, we "replace" the validated continuation bytes we've seen so far with +// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding +// behavior. The continuation byte check is included three times in the case +// where all of the continuation bytes for a character exist in the same buffer. +// It is also done this way as a slight performance increase instead of using a +// loop. +function utf8CheckExtraBytes(self, buf, p) { + if ((buf[0] & 0xC0) !== 0x80) { + self.lastNeed = 0; + return '\ufffd'; + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xC0) !== 0x80) { + self.lastNeed = 1; + return '\ufffd'; + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xC0) !== 0x80) { + self.lastNeed = 2; + return '\ufffd'; + } + } + } +} + +// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. +function utf8FillLast(buf) { + var p = this.lastTotal - this.lastNeed; + var r = utf8CheckExtraBytes(this, buf, p); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} + +// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a +// partial character, the character's bytes are buffered until the required +// number of bytes are available. +function utf8Text(buf, i) { + var total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString('utf8', i); + this.lastTotal = total; + var end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString('utf8', i, end); +} + +// For UTF-8, a replacement character is added when ending on a partial +// character. +function utf8End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + '\ufffd'; + return r; +} + +// UTF-16LE typically needs two bytes per character, but even if we have an even +// number of bytes available, we need to check if we end on a leading/high +// surrogate. In that case, we need to wait for the next two bytes in order to +// decode the last character properly. +function utf16Text(buf, i) { + if ((buf.length - i) % 2 === 0) { + var r = buf.toString('utf16le', i); + if (r) { + var c = r.charCodeAt(r.length - 1); + if (c >= 0xD800 && c <= 0xDBFF) { + this.lastNeed = 2; + this.lastTotal = 4; + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + return r.slice(0, -1); + } + } + return r; + } + this.lastNeed = 1; + this.lastTotal = 2; + this.lastChar[0] = buf[buf.length - 1]; + return buf.toString('utf16le', i, buf.length - 1); +} + +// For UTF-16LE we do not explicitly append special replacement characters if we +// end on a partial character, we simply let v8 handle that. +function utf16End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) { + var end = this.lastTotal - this.lastNeed; + return r + this.lastChar.toString('utf16le', 0, end); + } + return r; +} + +function base64Text(buf, i) { + var n = (buf.length - i) % 3; + if (n === 0) return buf.toString('base64', i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString('base64', i, buf.length - n); +} + +function base64End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); + return r; +} + +// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) +function simpleWrite(buf) { + return buf.toString(this.encoding); +} + +function simpleEnd(buf) { + return buf && buf.length ? this.write(buf) : ''; +} + +/***/ }), + +/***/ 681: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents a secret + * @param {Object} modem docker-modem + * @param {String} id Secret's id + */ +var Secret = function(modem, id) { + this.modem = modem; + this.id = id; +}; + +Secret.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Inspect + * @param {Function} callback Callback, if specified Docker will be queried. + * @return {Object} Name only if callback isn't specified. + */ +Secret.prototype.inspect = function(callback) { + var self = this; + + var optsf = { + path: '/secrets/' + this.id, + method: 'GET', + statusCodes: { + 200: true, + 404: 'secret not found', + 406: 'node is not part of a swarm', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Update a secret. + * + * @param {object} opts + * @param {function} callback + */ +Secret.prototype.update = function(opts, callback) { + var self = this; + if (!callback && typeof opts === 'function') { + callback = opts; + } + + var optsf = { + path: '/secrets/' + this.id + '/update?', + method: 'POST', + statusCodes: { + 200: true, + 404: 'secret not found', + 500: 'server error' + }, + options: opts + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + + +/** + * Removes the secret + * @param {[Object]} opts Remove options (optional) + * @param {Function} callback Callback + */ +Secret.prototype.remove = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/secrets/' + this.id, + method: 'DELETE', + statusCodes: { + 200: true, + 204: true, + 404: 'secret not found', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + + +module.exports = Secret; + + +/***/ }), + +/***/ 689: +/***/ (function(module, __unusedexports, __webpack_require__) { + +try { + var util = __webpack_require__(669); + /* istanbul ignore next */ + if (typeof util.inherits !== 'function') throw ''; + module.exports = util.inherits; +} catch (e) { + /* istanbul ignore next */ + module.exports = __webpack_require__(315); +} + + +/***/ }), + +/***/ 724: +/***/ (function(module) { + +module.exports = {"_from":"ssh2-streams@~0.4.10","_id":"ssh2-streams@0.4.10","_inBundle":false,"_integrity":"sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==","_location":"/ssh2-streams","_phantomChildren":{},"_requested":{"type":"range","registry":true,"raw":"ssh2-streams@~0.4.10","name":"ssh2-streams","escapedName":"ssh2-streams","rawSpec":"~0.4.10","saveSpec":null,"fetchSpec":"~0.4.10"},"_requiredBy":["/ssh2"],"_resolved":"https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz","_shasum":"48ef7e8a0e39d8f2921c30521d56dacb31d23a34","_spec":"ssh2-streams@~0.4.10","_where":"/var/home/vetra/Projects/run-gut-tests-action/node_modules/ssh2","author":{"name":"Brian White","email":"mscdex@mscdex.net"},"bugs":{"url":"https://github.com/mscdex/ssh2-streams/issues"},"bundleDependencies":false,"dependencies":{"asn1":"~0.2.0","bcrypt-pbkdf":"^1.0.2","streamsearch":"~0.1.2"},"deprecated":false,"description":"SSH2 and SFTP(v3) client/server protocol streams for node.js","engines":{"node":">=5.2.0"},"homepage":"https://github.com/mscdex/ssh2-streams#readme","keywords":["ssh","ssh2","sftp","secure","protocol","streams","client","server"],"licenses":[{"type":"MIT","url":"http://github.com/mscdex/ssh2-streams/raw/master/LICENSE"}],"main":"./index","name":"ssh2-streams","repository":{"type":"git","url":"git+ssh://git@github.com/mscdex/ssh2-streams.git"},"scripts":{"test":"node test/test.js"},"version":"0.4.10"}; + +/***/ }), + +/***/ 733: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2011 Mark Cavage All rights reserved. + +var assert = __webpack_require__(357); +var Buffer = __webpack_require__(215).Buffer; + +var ASN1 = __webpack_require__(362); +var errors = __webpack_require__(584); + + +// --- Globals + +var newInvalidAsn1Error = errors.newInvalidAsn1Error; + + + +// --- API + +function Reader(data) { + if (!data || !Buffer.isBuffer(data)) + throw new TypeError('data must be a node Buffer'); + + this._buf = data; + this._size = data.length; + + // These hold the "current" state + this._len = 0; + this._offset = 0; +} + +Object.defineProperty(Reader.prototype, 'length', { + enumerable: true, + get: function () { return (this._len); } +}); + +Object.defineProperty(Reader.prototype, 'offset', { + enumerable: true, + get: function () { return (this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'remain', { + get: function () { return (this._size - this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'buffer', { + get: function () { return (this._buf.slice(this._offset)); } +}); + + +/** + * Reads a single byte and advances offset; you can pass in `true` to make this + * a "peek" operation (i.e., get the byte, but don't advance the offset). + * + * @param {Boolean} peek true means don't move offset. + * @return {Number} the next byte, null if not enough data. + */ +Reader.prototype.readByte = function (peek) { + if (this._size - this._offset < 1) + return null; + + var b = this._buf[this._offset] & 0xff; + + if (!peek) + this._offset += 1; + + return b; +}; + + +Reader.prototype.peek = function () { + return this.readByte(true); +}; + + +/** + * Reads a (potentially) variable length off the BER buffer. This call is + * not really meant to be called directly, as callers have to manipulate + * the internal buffer afterwards. + * + * As a result of this call, you can call `Reader.length`, until the + * next thing called that does a readLength. + * + * @return {Number} the amount of offset to advance the buffer. + * @throws {InvalidAsn1Error} on bad ASN.1 + */ +Reader.prototype.readLength = function (offset) { + if (offset === undefined) + offset = this._offset; + + if (offset >= this._size) + return null; + + var lenB = this._buf[offset++] & 0xff; + if (lenB === null) + return null; + + if ((lenB & 0x80) === 0x80) { + lenB &= 0x7f; + + if (lenB === 0) + throw newInvalidAsn1Error('Indefinite length not supported'); + + if (lenB > 4) + throw newInvalidAsn1Error('encoding too long'); + + if (this._size - offset < lenB) + return null; + + this._len = 0; + for (var i = 0; i < lenB; i++) + this._len = (this._len << 8) + (this._buf[offset++] & 0xff); + + } else { + // Wasn't a variable length + this._len = lenB; + } + + return offset; +}; + + +/** + * Parses the next sequence in this BER buffer. + * + * To get the length of the sequence, call `Reader.length`. + * + * @return {Number} the sequence's tag. + */ +Reader.prototype.readSequence = function (tag) { + var seq = this.peek(); + if (seq === null) + return null; + if (tag !== undefined && tag !== seq) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + seq.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + this._offset = o; + return seq; +}; + + +Reader.prototype.readInt = function () { + return this._readTag(ASN1.Integer); +}; + + +Reader.prototype.readBoolean = function () { + return (this._readTag(ASN1.Boolean) === 0 ? false : true); +}; + + +Reader.prototype.readEnumeration = function () { + return this._readTag(ASN1.Enumeration); +}; + + +Reader.prototype.readString = function (tag, retbuf) { + if (!tag) + tag = ASN1.OctetString; + + var b = this.peek(); + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + + if (o === null) + return null; + + if (this.length > this._size - o) + return null; + + this._offset = o; + + if (this.length === 0) + return retbuf ? Buffer.alloc(0) : ''; + + var str = this._buf.slice(this._offset, this._offset + this.length); + this._offset += this.length; + + return retbuf ? str : str.toString('utf8'); +}; + +Reader.prototype.readOID = function (tag) { + if (!tag) + tag = ASN1.OID; + + var b = this.readString(tag, true); + if (b === null) + return null; + + var values = []; + var value = 0; + + for (var i = 0; i < b.length; i++) { + var byte = b[i] & 0xff; + + value <<= 7; + value += byte & 0x7f; + if ((byte & 0x80) === 0) { + values.push(value); + value = 0; + } + } + + value = values.shift(); + values.unshift(value % 40); + values.unshift((value / 40) >> 0); + + return values.join('.'); +}; + + +Reader.prototype._readTag = function (tag) { + assert.ok(tag !== undefined); + + var b = this.peek(); + + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + if (this.length > 4) + throw newInvalidAsn1Error('Integer too long: ' + this.length); + + if (this.length > this._size - o) + return null; + this._offset = o; + + var fb = this._buf[this._offset]; + var value = 0; + + for (var i = 0; i < this.length; i++) { + value <<= 8; + value |= (this._buf[this._offset++] & 0xff); + } + + if ((fb & 0x80) === 0x80 && i !== 4) + value -= (1 << (i * 8)); + + return value >> 0; +}; + + + +// --- Exported API + +module.exports = Reader; + + +/***/ }), + +/***/ 740: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; +// Ported from https://github.com/mafintosh/end-of-stream with +// permission from the author, Mathias Buus (@mafintosh). + + +var ERR_STREAM_PREMATURE_CLOSE = __webpack_require__(563).codes.ERR_STREAM_PREMATURE_CLOSE; + +function once(callback) { + var called = false; + return function () { + if (called) return; + called = true; + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + callback.apply(this, args); + }; +} + +function noop() {} + +function isRequest(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +} + +function eos(stream, opts, callback) { + if (typeof opts === 'function') return eos(stream, null, opts); + if (!opts) opts = {}; + callback = once(callback || noop); + var readable = opts.readable || opts.readable !== false && stream.readable; + var writable = opts.writable || opts.writable !== false && stream.writable; + + var onlegacyfinish = function onlegacyfinish() { + if (!stream.writable) onfinish(); + }; + + var writableEnded = stream._writableState && stream._writableState.finished; + + var onfinish = function onfinish() { + writable = false; + writableEnded = true; + if (!readable) callback.call(stream); + }; + + var readableEnded = stream._readableState && stream._readableState.endEmitted; + + var onend = function onend() { + readable = false; + readableEnded = true; + if (!writable) callback.call(stream); + }; + + var onerror = function onerror(err) { + callback.call(stream, err); + }; + + var onclose = function onclose() { + var err; + + if (readable && !readableEnded) { + if (!stream._readableState || !stream._readableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); + return callback.call(stream, err); + } + + if (writable && !writableEnded) { + if (!stream._writableState || !stream._writableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); + return callback.call(stream, err); + } + }; + + var onrequest = function onrequest() { + stream.req.on('finish', onfinish); + }; + + if (isRequest(stream)) { + stream.on('complete', onfinish); + stream.on('abort', onclose); + if (stream.req) onrequest();else stream.on('request', onrequest); + } else if (writable && !stream._writableState) { + // legacy streams + stream.on('end', onlegacyfinish); + stream.on('close', onlegacyfinish); + } + + stream.on('end', onend); + stream.on('finish', onfinish); + if (opts.error !== false) stream.on('error', onerror); + stream.on('close', onclose); + return function () { + stream.removeListener('complete', onfinish); + stream.removeListener('abort', onclose); + stream.removeListener('request', onrequest); + if (stream.req) stream.req.removeListener('finish', onfinish); + stream.removeListener('end', onlegacyfinish); + stream.removeListener('close', onlegacyfinish); + stream.removeListener('finish', onfinish); + stream.removeListener('end', onend); + stream.removeListener('error', onerror); + stream.removeListener('close', onclose); + }; +} + +module.exports = eos; + +/***/ }), + +/***/ 747: +/***/ (function(module) { + +module.exports = require("fs"); + +/***/ }), + +/***/ 757: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents an Exec + * @param {Object} modem docker-modem + * @param {String} id Exec's ID + */ +var Exec = function(modem, id) { + this.modem = modem; + this.id = id; +}; + +Exec.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Start the exec call that was setup. + * + * @param {object} opts + * @param {function} callback + */ +Exec.prototype.start = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/exec/' + this.id + '/start', + method: 'POST', + isStream: true, + allowEmpty: true, + hijack: args.opts.hijack, + openStdin: args.opts.stdin, + statusCodes: { + 200: true, + 204: true, + 404: 'no such exec', + 409: 'container stopped/paused', + 500: 'container not running' + }, + options: args.opts + }; + + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, data); + }); + } +}; + +/** + * Resize the exec call that was setup. + * + * @param {object} opts + * @param {function} callback + */ +Exec.prototype.resize = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/exec/' + this.id + '/resize?', + method: 'POST', + statusCodes: { + 200: true, + 404: 'no such exec', + 500: 'container not running' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, data); + }); + } +}; + +/** + * Get low-level information about the exec call. + * + * @param {function} callback + */ +Exec.prototype.inspect = function(callback) { + var self = this; + + var optsf = { + path: '/exec/' + this.id + '/json', + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such exec', + 500: 'server error' + } + }; + + if(callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return callback(err, data); + callback(err, data); + }); + } +}; + + +module.exports = Exec; + + +/***/ }), + +/***/ 761: +/***/ (function(module) { + +module.exports = require("zlib"); + +/***/ }), + +/***/ 780: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var Ber = __webpack_require__(62).Ber; + +var readUInt32BE = __webpack_require__(523).readUInt32BE; +var writeUInt32BE = __webpack_require__(523).writeUInt32BE; + +// XXX the value of 2400 from dropbear is only for certain strings, not all +// strings. for example the list strings used during handshakes +var MAX_STRING_LEN = Infinity;//2400; // taken from dropbear + +module.exports = { + iv_inc: iv_inc, + readInt: readInt, + readString: readString, + parseKey: __webpack_require__(798).parseKey, + sigSSHToASN1: sigSSHToASN1, + DSASigBERToBare: DSASigBERToBare, + ECDSASigASN1ToSSH: ECDSASigASN1ToSSH +}; + +function iv_inc(iv) { + var n = 12; + var c = 0; + do { + --n; + c = iv[n]; + if (c === 255) + iv[n] = 0; + else { + iv[n] = ++c; + return; + } + } while (n > 4); +} + +function readInt(buffer, start, stream, cb) { + var bufferLen = buffer.length; + if (start < 0 || start >= bufferLen || (bufferLen - start) < 4) { + stream && stream._cleanup(cb); + return false; + } + + return readUInt32BE(buffer, start); +} + +function DSASigBERToBare(signature) { + if (signature.length <= 40) + return signature; + // This is a quick and dirty way to get from BER encoded r and s that + // OpenSSL gives us, to just the bare values back to back (40 bytes + // total) like OpenSSH (and possibly others) are expecting + var asnReader = new Ber.Reader(signature); + asnReader.readSequence(); + var r = asnReader.readString(Ber.Integer, true); + var s = asnReader.readString(Ber.Integer, true); + var rOffset = 0; + var sOffset = 0; + if (r.length < 20) { + var rNew = Buffer.allocUnsafe(20); + r.copy(rNew, 1); + r = rNew; + r[0] = 0; + } + if (s.length < 20) { + var sNew = Buffer.allocUnsafe(20); + s.copy(sNew, 1); + s = sNew; + s[0] = 0; + } + if (r.length > 20 && r[0] === 0x00) + rOffset = 1; + if (s.length > 20 && s[0] === 0x00) + sOffset = 1; + var newSig = Buffer.allocUnsafe((r.length - rOffset) + (s.length - sOffset)); + r.copy(newSig, 0, rOffset); + s.copy(newSig, r.length - rOffset, sOffset); + return newSig; +} + +function ECDSASigASN1ToSSH(signature) { + if (signature[0] === 0x00) + return signature; + // Convert SSH signature parameters to ASN.1 BER values for OpenSSL + var asnReader = new Ber.Reader(signature); + asnReader.readSequence(); + var r = asnReader.readString(Ber.Integer, true); + var s = asnReader.readString(Ber.Integer, true); + if (r === null || s === null) + return false; + var newSig = Buffer.allocUnsafe(4 + r.length + 4 + s.length); + writeUInt32BE(newSig, r.length, 0); + r.copy(newSig, 4); + writeUInt32BE(newSig, s.length, 4 + r.length); + s.copy(newSig, 4 + 4 + r.length); + return newSig; +} + +function sigSSHToASN1(sig, type, self, callback) { + var asnWriter; + switch (type) { + case 'ssh-dss': + if (sig.length > 40) + return sig; + // Change bare signature r and s values to ASN.1 BER values for OpenSSL + asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + var r = sig.slice(0, 20); + var s = sig.slice(20); + if (r[0] & 0x80) { + var rNew = Buffer.allocUnsafe(21); + rNew[0] = 0x00; + r.copy(rNew, 1); + r = rNew; + } else if (r[0] === 0x00 && !(r[1] & 0x80)) { + r = r.slice(1); + } + if (s[0] & 0x80) { + var sNew = Buffer.allocUnsafe(21); + sNew[0] = 0x00; + s.copy(sNew, 1); + s = sNew; + } else if (s[0] === 0x00 && !(s[1] & 0x80)) { + s = s.slice(1); + } + asnWriter.writeBuffer(r, Ber.Integer); + asnWriter.writeBuffer(s, Ber.Integer); + asnWriter.endSequence(); + return asnWriter.buffer; + case 'ecdsa-sha2-nistp256': + case 'ecdsa-sha2-nistp384': + case 'ecdsa-sha2-nistp521': + var r = readString(sig, 0, self, callback); + if (r === false) + return false; + var s = readString(sig, sig._pos, self, callback); + if (s === false) + return false; + + asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + asnWriter.writeBuffer(r, Ber.Integer); + asnWriter.writeBuffer(s, Ber.Integer); + asnWriter.endSequence(); + return asnWriter.buffer; + default: + return sig; + } +} + +function readString(buffer, start, encoding, stream, cb, maxLen) { + if (encoding && !Buffer.isBuffer(encoding) && typeof encoding !== 'string') { + if (typeof cb === 'number') + maxLen = cb; + cb = stream; + stream = encoding; + encoding = undefined; + } + + start || (start = 0); + var bufferLen = buffer.length; + var left = (bufferLen - start); + var len; + var end; + if (start < 0 || start >= bufferLen || left < 4) { + stream && stream._cleanup(cb); + return false; + } + + len = readUInt32BE(buffer, start); + if (len > (maxLen || MAX_STRING_LEN) || left < (4 + len)) { + stream && stream._cleanup(cb); + return false; + } + + start += 4; + end = start + len; + buffer._pos = end; + + if (encoding) { + if (Buffer.isBuffer(encoding)) { + buffer.copy(encoding, 0, start, end); + return encoding; + } else { + return buffer.toString(encoding, start, end); + } + } else { + return buffer.slice(start, end); + } +} + + + +/***/ }), + +/***/ 784: +/***/ (function(module, __unusedexports, __webpack_require__) { + +/** + * Detect Electron renderer / nwjs process, which is node, but we should + * treat as a browser. + */ + +if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) { + module.exports = __webpack_require__(794); +} else { + module.exports = __webpack_require__(81); +} + + +/***/ }), + +/***/ 787: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var querystring = __webpack_require__(191), + http = __webpack_require__(498), + fs = __webpack_require__(747), + path = __webpack_require__(622), + url = __webpack_require__(835), + ssh = __webpack_require__(885), + HttpDuplex = __webpack_require__(23), + debug = __webpack_require__(784)('modem'), + utils = __webpack_require__(75), + util = __webpack_require__(669), + url = __webpack_require__(835), + splitca = __webpack_require__(895), + isWin = __webpack_require__(87).type() === 'Windows_NT'; + +var defaultOpts = function () { + var host; + var opts = {}; + + if (!process.env.DOCKER_HOST) { + // Windows socket path: //./pipe/docker_engine ( Windows 10 ) + // Linux & Darwin socket path: /var/run/docker.sock + opts.socketPath = isWin ? '//./pipe/docker_engine' : '/var/run/docker.sock'; + } else if (process.env.DOCKER_HOST.indexOf('unix://') === 0) { + // Strip off unix://, fall back to default of /var/run/docker.sock if + // unix:// was passed without a path + opts.socketPath = process.env.DOCKER_HOST.substring(7) || '/var/run/docker.sock'; + } else if (process.env.DOCKER_HOST.indexOf('npipe://') === 0) { + // Strip off npipe://, fall back to default of //./pipe/docker_engine if + // npipe:// was passed without a path + opts.socketPath = process.env.DOCKER_HOST.substring(8) || '//./pipe/docker_engine'; + } else { + var hostStr = process.env.DOCKER_HOST; + if (hostStr.indexOf('\/\/') < 0) { + hostStr = 'tcp://' + hostStr; + } + try { + host = new url.URL(hostStr); + } catch (err) { + throw new Error('DOCKER_HOST env variable should be something like tcp://localhost:1234'); + } + + opts.port = host.port; + + if (process.env.DOCKER_TLS_VERIFY === '1' || opts.port === '2376') { + opts.protocol = 'https'; + } else if (host.protocol === 'ssh:') { + opts.protocol = 'ssh'; + opts.username = host.username; + opts.sshAuthAgent = process.env.SSH_AUTH_SOCK; + } else { + opts.protocol = 'http'; + } + + opts.host = host.hostname; + + if (process.env.DOCKER_CERT_PATH) { + opts.ca = splitca(path.join(process.env.DOCKER_CERT_PATH, 'ca.pem')); + opts.cert = fs.readFileSync(path.join(process.env.DOCKER_CERT_PATH, 'cert.pem')); + opts.key = fs.readFileSync(path.join(process.env.DOCKER_CERT_PATH, 'key.pem')); + } + + if (process.env.DOCKER_CLIENT_TIMEOUT) { + opts.timeout = parseInt(process.env.DOCKER_CLIENT_TIMEOUT, 10); + } + } + + return opts; +}; + + +var Modem = function (options) { + var opts = Object.assign({}, defaultOpts(), options); + + this.host = opts.host; + + if(!this.host) { + this.socketPath = opts.socketPath; + } + + this.port = opts.port; + this.username = opts.username; + this.password = opts.password; + this.version = opts.version; + this.key = opts.key; + this.cert = opts.cert; + this.ca = opts.ca; + this.timeout = opts.timeout; + this.connectionTimeout = opts.connectionTimeout; + this.checkServerIdentity = opts.checkServerIdentity; + this.agent = opts.agent; + this.agentForward = opts.agentForward; + this.headers = opts.headers || {}; + this.sshAuthAgent = opts.sshAuthAgent; + + if (this.key && this.cert && this.ca) { + this.protocol = 'https'; + } + this.protocol = opts.protocol || this.protocol || 'http'; +}; + +Modem.prototype.dial = function (options, callback) { + var opts, address, data; + var self = this; + + if (options.options) { + opts = options.options; + } + + // Prevent credentials from showing up in URL + if (opts && opts.authconfig) { + delete opts.authconfig; + } + + if (this.version) { + options.path = '/' + this.version + options.path; + } + + if (this.host) { + var parsed = url.parse(self.host); + address = url.format({ + 'protocol': parsed.protocol || self.protocol, + 'hostname': parsed.hostname || self.host, + 'port': self.port + }); + address = url.resolve(address, options.path); + } else { + address = options.path; + } + + if (options.path.indexOf('?') !== -1) { + if (opts && Object.keys(opts).length > 0) { + address += this.buildQuerystring(opts._query || opts); + } else { + address = address.substring(0, address.length - 1); + } + } + + var optionsf = { + path: address, + method: options.method, + headers: options.headers || Object.assign({}, self.headers), + key: self.key, + cert: self.cert, + ca: self.ca + }; + + if (this.checkServerIdentity) { + optionsf.checkServerIdentity = this.checkServerIdentity; + } + + if (this.agent) { + optionsf.agent = this.agent; + } + + if (options.authconfig) { + optionsf.headers['X-Registry-Auth'] = options.authconfig.key || options.authconfig.base64 || + Buffer.from(JSON.stringify(options.authconfig)).toString('base64'); + } + + if (options.registryconfig) { + optionsf.headers['X-Registry-Config'] = options.registryconfig.base64 || + Buffer.from(JSON.stringify(options.registryconfig)).toString('base64'); + } + + if (options.file) { + if (typeof options.file === 'string') { + data = fs.createReadStream(path.resolve(options.file)); + } else { + data = options.file; + } + optionsf.headers['Content-Type'] = 'application/tar'; + } else if (opts && options.method === 'POST') { + data = JSON.stringify(opts._body || opts); + if (options.allowEmpty) { + optionsf.headers['Content-Type'] = 'application/json'; + } else { + if (data !== '{}' && data !== '""') { + optionsf.headers['Content-Type'] = 'application/json'; + } else { + data = undefined; + } + } + } + + if (typeof data === 'string') { + optionsf.headers['Content-Length'] = Buffer.byteLength(data); + } else if (Buffer.isBuffer(data) === true) { + optionsf.headers['Content-Length'] = data.length; + } else if (optionsf.method === 'PUT' || options.hijack || options.openStdin) { + optionsf.headers['Transfer-Encoding'] = 'chunked'; + } + + if (options.hijack) { + optionsf.headers.Connection = 'Upgrade'; + optionsf.headers.Upgrade = 'tcp'; + } + + if (this.socketPath) { + optionsf.socketPath = this.socketPath; + } else { + var urlp = url.parse(address); + optionsf.hostname = urlp.hostname; + optionsf.port = urlp.port; + optionsf.path = urlp.path; + } + + this.buildRequest(optionsf, options, data, callback); +}; + +Modem.prototype.buildRequest = function (options, context, data, callback) { + var self = this; + var connectionTimeoutTimer; + + var opts = self.protocol === 'ssh' ? Object.assign(options, { + agent: ssh({ 'host': self.host, 'port': self.port, 'username': self.username, 'password': self.password, 'agent': self.sshAuthAgent, 'agentForward': self.agentForward }), + protocol: 'http:' + }) : options; + + var req = http[self.protocol === 'ssh' ? 'http' : self.protocol].request(opts, function () { }); + + debug('Sending: %s', util.inspect(options, { + showHidden: true, + depth: null + })); + + if (self.connectionTimeout) { + connectionTimeoutTimer = setTimeout(function () { + debug('Connection Timeout of %s ms exceeded', self.connectionTimeout); + req.abort(); + }, self.connectionTimeout); + } + + if (self.timeout) { + req.on('socket', function (socket) { + socket.setTimeout(self.timeout); + socket.on('timeout', function () { + debug('Timeout of %s ms exceeded', self.timeout); + req.abort(); + }); + }); + } + + if (context.hijack === true) { + clearTimeout(connectionTimeoutTimer); + req.on('upgrade', function (res, sock, head) { + return callback(null, sock); + }); + } + + req.on('connect', function () { + clearTimeout(connectionTimeoutTimer); + }); + + req.on('disconnect', function () { + clearTimeout(connectionTimeoutTimer); + }); + + req.on('response', function (res) { + clearTimeout(connectionTimeoutTimer); + if (context.isStream === true) { + self.buildPayload(null, context.isStream, context.statusCodes, context.openStdin, req, res, null, callback); + } else { + var chunks = []; + res.on('data', function (chunk) { + chunks.push(chunk); + }); + + res.on('end', function () { + var buffer = Buffer.concat(chunks); + var result = buffer.toString(); + + debug('Received: %s', result); + + var json = utils.parseJSON(result) || buffer; + self.buildPayload(null, context.isStream, context.statusCodes, false, req, res, json, callback); + }); + } + }); + + req.on('error', function (error) { + clearTimeout(connectionTimeoutTimer); + self.buildPayload(error, context.isStream, context.statusCodes, false, {}, {}, null, callback); + }); + + if (typeof data === 'string' || Buffer.isBuffer(data)) { + req.write(data); + } else if (data) { + data.pipe(req); + } + + if (!context.hijack && !context.openStdin && (typeof data === 'string' || data === undefined || Buffer.isBuffer(data))) { + req.end(); + } +}; + +Modem.prototype.buildPayload = function (err, isStream, statusCodes, openStdin, req, res, json, cb) { + if (err) return cb(err, null); + + if (statusCodes[res.statusCode] !== true) { + getCause(isStream, res, json, function (err, cause) { + var msg = new Error( + '(HTTP code ' + res.statusCode + ') ' + + (statusCodes[res.statusCode] || 'unexpected') + ' - ' + + (cause.message || cause) + ' ' + ); + msg.reason = statusCodes[res.statusCode]; + msg.statusCode = res.statusCode; + msg.json = json; + cb(msg, null); + }); + } else { + if (openStdin) { + cb(null, new HttpDuplex(req, res)); + } else if (isStream) { + cb(null, res); + } else { + cb(null, json); + } + } + + function getCause(isStream, res, json, callback) { + var chunks = ''; + if (isStream) { + res.on('data', function (chunk) { + chunks += chunk; + }); + res.on('end', function () { + callback(null, utils.parseJSON(chunks) || chunks); + }); + } else { + callback(null, json); + } + } +}; + +Modem.prototype.demuxStream = function (stream, stdout, stderr) { + var nextDataType = null; + var nextDataLength = null; + var buffer = Buffer.from(''); + function processData(data) { + if (data) { + buffer = Buffer.concat([buffer, data]); + } + if (!nextDataType) { + if (buffer.length >= 8) { + var header = bufferSlice(8); + nextDataType = header.readUInt8(0); + nextDataLength = header.readUInt32BE(4); + // It's possible we got a "data" that contains multiple messages + // Process the next one + processData(); + } + } else { + if (buffer.length >= nextDataLength) { + var content = bufferSlice(nextDataLength); + if (nextDataType === 1) { + stdout.write(content); + } else { + stderr.write(content); + } + nextDataType = null; + // It's possible we got a "data" that contains multiple messages + // Process the next one + processData(); + } + } + } + + function bufferSlice(end) { + var out = buffer.slice(0, end); + buffer = Buffer.from(buffer.slice(end, buffer.length)); + return out; + } + + stream.on('data', processData); +}; + +Modem.prototype.followProgress = function (stream, onFinished, onProgress) { + var buf = ''; + var output = []; + var finished = false; + + stream.on('data', onStreamEvent); + stream.on('error', onStreamError); + stream.on('end', onStreamEnd); + stream.on('close', onStreamEnd); + + function onStreamEvent(data) { + buf += data.toString(); + pump(); + + function pump() { + var pos; + while ((pos = buf.indexOf('\n')) >= 0) { + if (pos == 0) { + buf = buf.slice(1); + continue; + } + processLine(buf.slice(0, pos)); + buf = buf.slice(pos + 1); + } + } + + function processLine(line) { + if (line[line.length - 1] == '\r') line = line.substr(0, line.length - 1); + if (line.length > 0) { + var obj = JSON.parse(line); + output.push(obj); + if (onProgress) { + onProgress(obj); + } + } + } + }; + + function onStreamError(err) { + finished = true; + stream.removeListener('data', onStreamEvent); + stream.removeListener('error', onStreamError); + stream.removeListener('end', onStreamEnd); + stream.removeListener('close', onStreamEnd); + onFinished(err, output); + } + + function onStreamEnd() { + if(!finished) onFinished(null, output); + finished = true; + } +}; + +Modem.prototype.buildQuerystring = function (opts) { + var clone = {}; + + // serialize map values as JSON strings, else querystring truncates. + Object.keys(opts).map(function (key, i) { + clone[key] = opts[key] && typeof opts[key] === 'object' && key !== 't' ? + JSON.stringify(opts[key]) : opts[key]; + }); + + return querystring.stringify(clone); +}; + +module.exports = Modem; + + +/***/ }), + +/***/ 794: +/***/ (function(module, exports, __webpack_require__) { + +/* eslint-env browser */ + +/** + * This is the web browser implementation of `debug()`. + */ + +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = localstorage(); + +/** + * Colors. + */ + +exports.colors = [ + '#0000CC', + '#0000FF', + '#0033CC', + '#0033FF', + '#0066CC', + '#0066FF', + '#0099CC', + '#0099FF', + '#00CC00', + '#00CC33', + '#00CC66', + '#00CC99', + '#00CCCC', + '#00CCFF', + '#3300CC', + '#3300FF', + '#3333CC', + '#3333FF', + '#3366CC', + '#3366FF', + '#3399CC', + '#3399FF', + '#33CC00', + '#33CC33', + '#33CC66', + '#33CC99', + '#33CCCC', + '#33CCFF', + '#6600CC', + '#6600FF', + '#6633CC', + '#6633FF', + '#66CC00', + '#66CC33', + '#9900CC', + '#9900FF', + '#9933CC', + '#9933FF', + '#99CC00', + '#99CC33', + '#CC0000', + '#CC0033', + '#CC0066', + '#CC0099', + '#CC00CC', + '#CC00FF', + '#CC3300', + '#CC3333', + '#CC3366', + '#CC3399', + '#CC33CC', + '#CC33FF', + '#CC6600', + '#CC6633', + '#CC9900', + '#CC9933', + '#CCCC00', + '#CCCC33', + '#FF0000', + '#FF0033', + '#FF0066', + '#FF0099', + '#FF00CC', + '#FF00FF', + '#FF3300', + '#FF3333', + '#FF3366', + '#FF3399', + '#FF33CC', + '#FF33FF', + '#FF6600', + '#FF6633', + '#FF9900', + '#FF9933', + '#FFCC00', + '#FFCC33' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +// eslint-disable-next-line complexity +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { + return true; + } + + // Internet Explorer and Edge do not support colors. + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } + + // Is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || + // Is firebug? http://stackoverflow.com/a/398120/376773 + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || + // Is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + // Double check webkit in userAgent just in case we are in a worker + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); +} + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs(args) { + args[0] = (this.useColors ? '%c' : '') + + this.namespace + + (this.useColors ? ' %c' : ' ') + + args[0] + + (this.useColors ? '%c ' : ' ') + + '+' + module.exports.humanize(this.diff); + + if (!this.useColors) { + return; + } + + const c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit'); + + // The final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + let index = 0; + let lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, match => { + if (match === '%%') { + return; + } + index++; + if (match === '%c') { + // We only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ +function log(...args) { + // This hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return typeof console === 'object' && + console.log && + console.log(...args); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ +function save(namespaces) { + try { + if (namespaces) { + exports.storage.setItem('debug', namespaces); + } else { + exports.storage.removeItem('debug'); + } + } catch (error) { + // Swallow + // XXX (@Qix-) should we be logging these? + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ +function load() { + let r; + try { + r = exports.storage.getItem('debug'); + } catch (error) { + // Swallow + // XXX (@Qix-) should we be logging these? + } + + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + + return r; +} + +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage() { + try { + // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context + // The Browser also has localStorage in the global context. + return localStorage; + } catch (error) { + // Swallow + // XXX (@Qix-) should we be logging these? + } +} + +module.exports = __webpack_require__(486)(exports); + +const {formatters} = module.exports; + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +formatters.j = function (v) { + try { + return JSON.stringify(v); + } catch (error) { + return '[UnexpectedJSONParseError]: ' + error.message; + } +}; + + +/***/ }), + +/***/ 798: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// TODO: +// * utilize `crypto.create(Private|Public)Key()` and `keyObject.export()` +// * handle multi-line header values (OpenSSH)? +// * more thorough validation? + +var crypto = __webpack_require__(417); +var cryptoSign = crypto.sign; +var cryptoVerify = crypto.verify; +var createSign = crypto.createSign; +var createVerify = crypto.createVerify; +var createDecipheriv = crypto.createDecipheriv; +var createHash = crypto.createHash; +var createHmac = crypto.createHmac; +var supportedOpenSSLCiphers = crypto.getCiphers(); + +var utils; +var Ber = __webpack_require__(62).Ber; +var bcrypt_pbkdf = __webpack_require__(641).pbkdf; + +var bufferHelpers = __webpack_require__(523); +var readUInt32BE = bufferHelpers.readUInt32BE; +var writeUInt32BE = bufferHelpers.writeUInt32BE; +var constants = __webpack_require__(801); +var SUPPORTED_CIPHER = constants.ALGORITHMS.SUPPORTED_CIPHER; +var CIPHER_INFO = constants.CIPHER_INFO; +var SSH_TO_OPENSSL = constants.SSH_TO_OPENSSL; +var EDDSA_SUPPORTED = constants.EDDSA_SUPPORTED; + +var SYM_HASH_ALGO = Symbol('Hash Algorithm'); +var SYM_PRIV_PEM = Symbol('Private key PEM'); +var SYM_PUB_PEM = Symbol('Public key PEM'); +var SYM_PUB_SSH = Symbol('Public key SSH'); +var SYM_DECRYPTED = Symbol('Decrypted Key'); + +// Create OpenSSL cipher name -> SSH cipher name conversion table +var CIPHER_INFO_OPENSSL = Object.create(null); +(function() { + var keys = Object.keys(CIPHER_INFO); + for (var i = 0; i < keys.length; ++i) { + var cipherName = SSH_TO_OPENSSL[keys[i]]; + if (!cipherName || CIPHER_INFO_OPENSSL[cipherName]) + continue; + CIPHER_INFO_OPENSSL[cipherName] = CIPHER_INFO[keys[i]]; + } +})(); + +var trimStart = (function() { + if (typeof String.prototype.trimStart === 'function') { + return function trimStart(str) { + return str.trimStart(); + }; + } + + return function trimStart(str) { + var start = 0; + for (var i = 0; i < str.length; ++i) { + switch (str.charCodeAt(i)) { + case 32: // ' ' + case 9: // '\t' + case 13: // '\r' + case 10: // '\n' + case 12: // '\f' + ++start; + continue; + } + break; + } + if (start === 0) + return str; + return str.slice(start); + }; +})(); + +function makePEM(type, data) { + data = data.toString('base64'); + return '-----BEGIN ' + type + ' KEY-----\n' + + data.replace(/.{64}/g, '$&\n') + + (data.length % 64 ? '\n' : '') + + '-----END ' + type + ' KEY-----'; +} + +function combineBuffers(buf1, buf2) { + var result = Buffer.allocUnsafe(buf1.length + buf2.length); + buf1.copy(result, 0); + buf2.copy(result, buf1.length); + return result; +} + +function skipFields(buf, nfields) { + var bufLen = buf.length; + var pos = (buf._pos || 0); + for (var i = 0; i < nfields; ++i) { + var left = (bufLen - pos); + if (pos >= bufLen || left < 4) + return false; + var len = readUInt32BE(buf, pos); + if (left < 4 + len) + return false; + pos += 4 + len; + } + buf._pos = pos; + return true; +} + +function genOpenSSLRSAPub(n, e) { + var asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + // algorithm + asnWriter.startSequence(); + asnWriter.writeOID('1.2.840.113549.1.1.1'); // rsaEncryption + // algorithm parameters (RSA has none) + asnWriter.writeNull(); + asnWriter.endSequence(); + + // subjectPublicKey + asnWriter.startSequence(Ber.BitString); + asnWriter.writeByte(0x00); + asnWriter.startSequence(); + asnWriter.writeBuffer(n, Ber.Integer); + asnWriter.writeBuffer(e, Ber.Integer); + asnWriter.endSequence(); + asnWriter.endSequence(); + asnWriter.endSequence(); + return makePEM('PUBLIC', asnWriter.buffer); +} + +function genOpenSSHRSAPub(n, e) { + var publicKey = Buffer.allocUnsafe(4 + 7 // "ssh-rsa" + + 4 + n.length + + 4 + e.length); + + writeUInt32BE(publicKey, 7, 0); + publicKey.write('ssh-rsa', 4, 7, 'ascii'); + + var i = 4 + 7; + writeUInt32BE(publicKey, e.length, i); + e.copy(publicKey, i += 4); + + writeUInt32BE(publicKey, n.length, i += e.length); + n.copy(publicKey, i + 4); + + return publicKey; +} + +var genOpenSSLRSAPriv = (function() { + function genRSAASN1Buf(n, e, d, p, q, dmp1, dmq1, iqmp) { + var asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + asnWriter.writeInt(0x00, Ber.Integer); + asnWriter.writeBuffer(n, Ber.Integer); + asnWriter.writeBuffer(e, Ber.Integer); + asnWriter.writeBuffer(d, Ber.Integer); + asnWriter.writeBuffer(p, Ber.Integer); + asnWriter.writeBuffer(q, Ber.Integer); + asnWriter.writeBuffer(dmp1, Ber.Integer); + asnWriter.writeBuffer(dmq1, Ber.Integer); + asnWriter.writeBuffer(iqmp, Ber.Integer); + asnWriter.endSequence(); + return asnWriter.buffer; + } + + function bigIntFromBuffer(buf) { + return BigInt('0x' + buf.toString('hex')); + } + + function bigIntToBuffer(bn) { + var hex = bn.toString(16); + if ((hex.length & 1) !== 0) { + hex = '0' + hex; + } else { + var sigbit = hex.charCodeAt(0); + // BER/DER integers require leading zero byte to denote a positive value + // when first byte >= 0x80 + if (sigbit === 56 || (sigbit >= 97 && sigbit <= 102)) + hex = '00' + hex; + } + return Buffer.from(hex, 'hex'); + } + + // Feature detect native BigInt availability and use it when possible + try { + var code = [ + 'return function genOpenSSLRSAPriv(n, e, d, iqmp, p, q) {', + ' var bn_d = bigIntFromBuffer(d);', + ' var dmp1 = bigIntToBuffer(bn_d % (bigIntFromBuffer(p) - 1n));', + ' var dmq1 = bigIntToBuffer(bn_d % (bigIntFromBuffer(q) - 1n));', + ' return makePEM(\'RSA PRIVATE\', ' + + 'genRSAASN1Buf(n, e, d, p, q, dmp1, dmq1, iqmp));', + '};' + ].join('\n'); + return new Function( + 'bigIntFromBuffer, bigIntToBuffer, makePEM, genRSAASN1Buf', + code + )(bigIntFromBuffer, bigIntToBuffer, makePEM, genRSAASN1Buf); + } catch (ex) { + return (function() { + var BigInteger = __webpack_require__(652); + return function genOpenSSLRSAPriv(n, e, d, iqmp, p, q) { + var pbi = new BigInteger(p, 256); + var qbi = new BigInteger(q, 256); + var dbi = new BigInteger(d, 256); + var dmp1bi = dbi.mod(pbi.subtract(BigInteger.ONE)); + var dmq1bi = dbi.mod(qbi.subtract(BigInteger.ONE)); + var dmp1 = Buffer.from(dmp1bi.toByteArray()); + var dmq1 = Buffer.from(dmq1bi.toByteArray()); + return makePEM('RSA PRIVATE', + genRSAASN1Buf(n, e, d, p, q, dmp1, dmq1, iqmp)); + }; + })(); + } +})(); + +function genOpenSSLDSAPub(p, q, g, y) { + var asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + // algorithm + asnWriter.startSequence(); + asnWriter.writeOID('1.2.840.10040.4.1'); // id-dsa + // algorithm parameters + asnWriter.startSequence(); + asnWriter.writeBuffer(p, Ber.Integer); + asnWriter.writeBuffer(q, Ber.Integer); + asnWriter.writeBuffer(g, Ber.Integer); + asnWriter.endSequence(); + asnWriter.endSequence(); + + // subjectPublicKey + asnWriter.startSequence(Ber.BitString); + asnWriter.writeByte(0x00); + asnWriter.writeBuffer(y, Ber.Integer); + asnWriter.endSequence(); + asnWriter.endSequence(); + return makePEM('PUBLIC', asnWriter.buffer); +} + +function genOpenSSHDSAPub(p, q, g, y) { + var publicKey = Buffer.allocUnsafe(4 + 7 // ssh-dss + + 4 + p.length + + 4 + q.length + + 4 + g.length + + 4 + y.length); + + writeUInt32BE(publicKey, 7, 0); + publicKey.write('ssh-dss', 4, 7, 'ascii'); + + var i = 4 + 7; + writeUInt32BE(publicKey, p.length, i); + p.copy(publicKey, i += 4); + + writeUInt32BE(publicKey, q.length, i += p.length); + q.copy(publicKey, i += 4); + + writeUInt32BE(publicKey, g.length, i += q.length); + g.copy(publicKey, i += 4); + + writeUInt32BE(publicKey, y.length, i += g.length); + y.copy(publicKey, i + 4); + + return publicKey; +} + +function genOpenSSLDSAPriv(p, q, g, y, x) { + var asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + asnWriter.writeInt(0x00, Ber.Integer); + asnWriter.writeBuffer(p, Ber.Integer); + asnWriter.writeBuffer(q, Ber.Integer); + asnWriter.writeBuffer(g, Ber.Integer); + asnWriter.writeBuffer(y, Ber.Integer); + asnWriter.writeBuffer(x, Ber.Integer); + asnWriter.endSequence(); + return makePEM('DSA PRIVATE', asnWriter.buffer); +} + +function genOpenSSLEdPub(pub) { + var asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + // algorithm + asnWriter.startSequence(); + asnWriter.writeOID('1.3.101.112'); // id-Ed25519 + asnWriter.endSequence(); + + // PublicKey + asnWriter.startSequence(Ber.BitString); + asnWriter.writeByte(0x00); + // XXX: hack to write a raw buffer without a tag -- yuck + asnWriter._ensure(pub.length); + pub.copy(asnWriter._buf, asnWriter._offset, 0, pub.length); + asnWriter._offset += pub.length; + asnWriter.endSequence(); + asnWriter.endSequence(); + return makePEM('PUBLIC', asnWriter.buffer); +} + +function genOpenSSHEdPub(pub) { + var publicKey = Buffer.allocUnsafe(4 + 11 // ssh-ed25519 + + 4 + pub.length); + + writeUInt32BE(publicKey, 11, 0); + publicKey.write('ssh-ed25519', 4, 11, 'ascii'); + + writeUInt32BE(publicKey, pub.length, 15); + pub.copy(publicKey, 19); + + return publicKey; +} + +function genOpenSSLEdPriv(priv) { + var asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + // version + asnWriter.writeInt(0x00, Ber.Integer); + + // algorithm + asnWriter.startSequence(); + asnWriter.writeOID('1.3.101.112'); // id-Ed25519 + asnWriter.endSequence(); + + // PrivateKey + asnWriter.startSequence(Ber.OctetString); + asnWriter.writeBuffer(priv, Ber.OctetString); + asnWriter.endSequence(); + asnWriter.endSequence(); + return makePEM('PRIVATE', asnWriter.buffer); +} + +function genOpenSSLECDSAPub(oid, Q) { + var asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + // algorithm + asnWriter.startSequence(); + asnWriter.writeOID('1.2.840.10045.2.1'); // id-ecPublicKey + // algorithm parameters (namedCurve) + asnWriter.writeOID(oid); + asnWriter.endSequence(); + + // subjectPublicKey + asnWriter.startSequence(Ber.BitString); + asnWriter.writeByte(0x00); + // XXX: hack to write a raw buffer without a tag -- yuck + asnWriter._ensure(Q.length); + Q.copy(asnWriter._buf, asnWriter._offset, 0, Q.length); + asnWriter._offset += Q.length; + // end hack + asnWriter.endSequence(); + asnWriter.endSequence(); + return makePEM('PUBLIC', asnWriter.buffer); +} + +function genOpenSSHECDSAPub(oid, Q) { + var curveName; + switch (oid) { + case '1.2.840.10045.3.1.7': + // prime256v1/secp256r1 + curveName = 'nistp256'; + break; + case '1.3.132.0.34': + // secp384r1 + curveName = 'nistp384'; + break; + case '1.3.132.0.35': + // secp521r1 + curveName = 'nistp521'; + break; + default: + return; + } + + var publicKey = Buffer.allocUnsafe(4 + 19 // ecdsa-sha2- + + 4 + 8 // + + 4 + Q.length); + + writeUInt32BE(publicKey, 19, 0); + publicKey.write('ecdsa-sha2-' + curveName, 4, 19, 'ascii'); + + writeUInt32BE(publicKey, 8, 23); + publicKey.write(curveName, 27, 8, 'ascii'); + + writeUInt32BE(publicKey, Q.length, 35); + Q.copy(publicKey, 39); + + return publicKey; +} + +function genOpenSSLECDSAPriv(oid, pub, priv) { + var asnWriter = new Ber.Writer(); + asnWriter.startSequence(); + // version + asnWriter.writeInt(0x01, Ber.Integer); + // privateKey + asnWriter.writeBuffer(priv, Ber.OctetString); + // parameters (optional) + asnWriter.startSequence(0xA0); + asnWriter.writeOID(oid); + asnWriter.endSequence(); + // publicKey (optional) + asnWriter.startSequence(0xA1); + asnWriter.startSequence(Ber.BitString); + asnWriter.writeByte(0x00); + // XXX: hack to write a raw buffer without a tag -- yuck + asnWriter._ensure(pub.length); + pub.copy(asnWriter._buf, asnWriter._offset, 0, pub.length); + asnWriter._offset += pub.length; + // end hack + asnWriter.endSequence(); + asnWriter.endSequence(); + asnWriter.endSequence(); + return makePEM('EC PRIVATE', asnWriter.buffer); +} + +function genOpenSSLECDSAPubFromPriv(curveName, priv) { + var tempECDH = crypto.createECDH(curveName); + tempECDH.setPrivateKey(priv); + return tempECDH.getPublicKey(); +} + +var baseKeySign = (function() { + if (typeof cryptoSign === 'function') { + return function sign(data) { + var pem = this[SYM_PRIV_PEM]; + if (pem === null) + return new Error('No private key available'); + try { + return cryptoSign(this[SYM_HASH_ALGO], data, pem); + } catch (ex) { + return ex; + } + }; + } else { + function trySign(signature, privKey) { + try { + return signature.sign(privKey); + } catch (ex) { + return ex; + } + } + + return function sign(data) { + var pem = this[SYM_PRIV_PEM]; + if (pem === null) + return new Error('No private key available'); + var signature = createSign(this[SYM_HASH_ALGO]); + signature.update(data); + return trySign(signature, pem); + }; + } +})(); + +var baseKeyVerify = (function() { + if (typeof cryptoVerify === 'function') { + return function verify(data, signature) { + var pem = this[SYM_PUB_PEM]; + if (pem === null) + return new Error('No public key available'); + try { + return cryptoVerify(this[SYM_HASH_ALGO], data, pem, signature); + } catch (ex) { + return ex; + } + }; + } else { + function tryVerify(verifier, pubKey, signature) { + try { + return verifier.verify(pubKey, signature); + } catch (ex) { + return ex; + } + } + + return function verify(data, signature) { + var pem = this[SYM_PUB_PEM]; + if (pem === null) + return new Error('No public key available'); + var verifier = createVerify(this[SYM_HASH_ALGO]); + verifier.update(data); + return tryVerify(verifier, pem, signature); + }; + } +})(); + +var BaseKey = { + sign: baseKeySign, + verify: baseKeyVerify, + getPrivatePEM: function getPrivatePEM() { + return this[SYM_PRIV_PEM]; + }, + getPublicPEM: function getPublicPEM() { + return this[SYM_PUB_PEM]; + }, + getPublicSSH: function getPublicSSH() { + return this[SYM_PUB_SSH]; + }, +}; + + + +function OpenSSH_Private(type, comment, privPEM, pubPEM, pubSSH, algo, decrypted) { + this.type = type; + this.comment = comment; + this[SYM_PRIV_PEM] = privPEM; + this[SYM_PUB_PEM] = pubPEM; + this[SYM_PUB_SSH] = pubSSH; + this[SYM_HASH_ALGO] = algo; + this[SYM_DECRYPTED] = decrypted; +} +OpenSSH_Private.prototype = BaseKey; +(function() { + var regexp = /^-----BEGIN OPENSSH PRIVATE KEY-----(?:\r\n|\n)([\s\S]+)(?:\r\n|\n)-----END OPENSSH PRIVATE KEY-----$/; + OpenSSH_Private.parse = function(str, passphrase) { + var m = regexp.exec(str); + if (m === null) + return null; + var ret; + var data = Buffer.from(m[1], 'base64'); + if (data.length < 31) // magic (+ magic null term.) + minimum field lengths + return new Error('Malformed OpenSSH private key'); + var magic = data.toString('ascii', 0, 15); + if (magic !== 'openssh-key-v1\0') + return new Error('Unsupported OpenSSH key magic: ' + magic); + + // avoid cyclic require by requiring on first use + if (!utils) + utils = __webpack_require__(780); + + var cipherName = utils.readString(data, 15, 'ascii'); + if (cipherName === false) + return new Error('Malformed OpenSSH private key'); + if (cipherName !== 'none' && SUPPORTED_CIPHER.indexOf(cipherName) === -1) + return new Error('Unsupported cipher for OpenSSH key: ' + cipherName); + + var kdfName = utils.readString(data, data._pos, 'ascii'); + if (kdfName === false) + return new Error('Malformed OpenSSH private key'); + if (kdfName !== 'none') { + if (cipherName === 'none') + return new Error('Malformed OpenSSH private key'); + if (kdfName !== 'bcrypt') + return new Error('Unsupported kdf name for OpenSSH key: ' + kdfName); + if (!passphrase) { + return new Error( + 'Encrypted private OpenSSH key detected, but no passphrase given' + ); + } + } else if (cipherName !== 'none') { + return new Error('Malformed OpenSSH private key'); + } + + var encInfo; + var cipherKey; + var cipherIV; + if (cipherName !== 'none') + encInfo = CIPHER_INFO[cipherName]; + var kdfOptions = utils.readString(data, data._pos); + if (kdfOptions === false) + return new Error('Malformed OpenSSH private key'); + if (kdfOptions.length) { + switch (kdfName) { + case 'none': + return new Error('Malformed OpenSSH private key'); + case 'bcrypt': + /* + string salt + uint32 rounds + */ + var salt = utils.readString(kdfOptions, 0); + if (salt === false || kdfOptions._pos + 4 > kdfOptions.length) + return new Error('Malformed OpenSSH private key'); + var rounds = readUInt32BE(kdfOptions, kdfOptions._pos); + var gen = Buffer.allocUnsafe(encInfo.keyLen + encInfo.ivLen); + var r = bcrypt_pbkdf(passphrase, + passphrase.length, + salt, + salt.length, + gen, + gen.length, + rounds); + if (r !== 0) + return new Error('Failed to generate information to decrypt key'); + cipherKey = gen.slice(0, encInfo.keyLen); + cipherIV = gen.slice(encInfo.keyLen); + break; + } + } else if (kdfName !== 'none') { + return new Error('Malformed OpenSSH private key'); + } + + var keyCount = utils.readInt(data, data._pos); + if (keyCount === false) + return new Error('Malformed OpenSSH private key'); + data._pos += 4; + + if (keyCount > 0) { + // TODO: place sensible limit on max `keyCount` + + // Read public keys first + for (var i = 0; i < keyCount; ++i) { + var pubData = utils.readString(data, data._pos); + if (pubData === false) + return new Error('Malformed OpenSSH private key'); + var type = utils.readString(pubData, 0, 'ascii'); + if (type === false) + return new Error('Malformed OpenSSH private key'); + } + + var privBlob = utils.readString(data, data._pos); + if (privBlob === false) + return new Error('Malformed OpenSSH private key'); + + if (cipherKey !== undefined) { + // encrypted private key(s) + if (privBlob.length < encInfo.blockLen + || (privBlob.length % encInfo.blockLen) !== 0) { + return new Error('Malformed OpenSSH private key'); + } + try { + var options = { authTagLength: encInfo.authLen }; + var decipher = createDecipheriv(SSH_TO_OPENSSL[cipherName], + cipherKey, + cipherIV, + options); + if (encInfo.authLen > 0) { + if (data.length - data._pos < encInfo.authLen) + return new Error('Malformed OpenSSH private key'); + decipher.setAuthTag( + data.slice(data._pos, data._pos += encInfo.authLen) + ); + } + privBlob = combineBuffers(decipher.update(privBlob), + decipher.final()); + } catch (ex) { + return ex; + } + } + // Nothing should we follow the private key(s), except a possible + // authentication tag for relevant ciphers + if (data._pos !== data.length) + return new Error('Malformed OpenSSH private key'); + + ret = parseOpenSSHPrivKeys(privBlob, keyCount, cipherKey !== undefined); + } else { + ret = []; + } + return ret; + }; + + function parseOpenSSHPrivKeys(data, nkeys, decrypted) { + var keys = []; + /* + uint32 checkint + uint32 checkint + string privatekey1 + string comment1 + string privatekey2 + string comment2 + ... + string privatekeyN + string commentN + char 1 + char 2 + char 3 + ... + char padlen % 255 + */ + if (data.length < 8) + return new Error('Malformed OpenSSH private key'); + var check1 = readUInt32BE(data, 0); + var check2 = readUInt32BE(data, 4); + if (check1 !== check2) { + if (decrypted) + return new Error('OpenSSH key integrity check failed -- bad passphrase?'); + return new Error('OpenSSH key integrity check failed'); + } + data._pos = 8; + var i; + var oid; + for (i = 0; i < nkeys; ++i) { + var algo = undefined; + var privPEM = undefined; + var pubPEM = undefined; + var pubSSH = undefined; + // The OpenSSH documentation for the key format actually lies, the entirety + // of the private key content is not contained with a string field, it's + // actually the literal contents of the private key, so to be able to find + // the end of the key data you need to know the layout/format of each key + // type ... + var type = utils.readString(data, data._pos, 'ascii'); + if (type === false) + return new Error('Malformed OpenSSH private key'); + + switch (type) { + case 'ssh-rsa': + /* + string n -- public + string e -- public + string d -- private + string iqmp -- private + string p -- private + string q -- private + */ + var n = utils.readString(data, data._pos); + if (n === false) + return new Error('Malformed OpenSSH private key'); + var e = utils.readString(data, data._pos); + if (e === false) + return new Error('Malformed OpenSSH private key'); + var d = utils.readString(data, data._pos); + if (d === false) + return new Error('Malformed OpenSSH private key'); + var iqmp = utils.readString(data, data._pos); + if (iqmp === false) + return new Error('Malformed OpenSSH private key'); + var p = utils.readString(data, data._pos); + if (p === false) + return new Error('Malformed OpenSSH private key'); + var q = utils.readString(data, data._pos); + if (q === false) + return new Error('Malformed OpenSSH private key'); + + pubPEM = genOpenSSLRSAPub(n, e); + pubSSH = genOpenSSHRSAPub(n, e); + privPEM = genOpenSSLRSAPriv(n, e, d, iqmp, p, q); + algo = 'sha1'; + break; + case 'ssh-dss': + /* + string p -- public + string q -- public + string g -- public + string y -- public + string x -- private + */ + var p = utils.readString(data, data._pos); + if (p === false) + return new Error('Malformed OpenSSH private key'); + var q = utils.readString(data, data._pos); + if (q === false) + return new Error('Malformed OpenSSH private key'); + var g = utils.readString(data, data._pos); + if (g === false) + return new Error('Malformed OpenSSH private key'); + var y = utils.readString(data, data._pos); + if (y === false) + return new Error('Malformed OpenSSH private key'); + var x = utils.readString(data, data._pos); + if (x === false) + return new Error('Malformed OpenSSH private key'); + + pubPEM = genOpenSSLDSAPub(p, q, g, y); + pubSSH = genOpenSSHDSAPub(p, q, g, y); + privPEM = genOpenSSLDSAPriv(p, q, g, y, x); + algo = 'sha1'; + break; + case 'ssh-ed25519': + if (!EDDSA_SUPPORTED) + return new Error('Unsupported OpenSSH private key type: ' + type); + /* + * string public key + * string private key + public key + */ + var edpub = utils.readString(data, data._pos); + if (edpub === false || edpub.length !== 32) + return new Error('Malformed OpenSSH private key'); + var edpriv = utils.readString(data, data._pos); + if (edpriv === false || edpriv.length !== 64) + return new Error('Malformed OpenSSH private key'); + + pubPEM = genOpenSSLEdPub(edpub); + pubSSH = genOpenSSHEdPub(edpub); + privPEM = genOpenSSLEdPriv(edpriv.slice(0, 32)); + algo = null; + break; + case 'ecdsa-sha2-nistp256': + algo = 'sha256'; + oid = '1.2.840.10045.3.1.7'; + case 'ecdsa-sha2-nistp384': + if (algo === undefined) { + algo = 'sha384'; + oid = '1.3.132.0.34'; + } + case 'ecdsa-sha2-nistp521': + if (algo === undefined) { + algo = 'sha512'; + oid = '1.3.132.0.35'; + } + /* + string curve name + string Q -- public + string d -- private + */ + // TODO: validate curve name against type + if (!skipFields(data, 1)) // Skip curve name + return new Error('Malformed OpenSSH private key'); + var ecpub = utils.readString(data, data._pos); + if (ecpub === false) + return new Error('Malformed OpenSSH private key'); + var ecpriv = utils.readString(data, data._pos); + if (ecpriv === false) + return new Error('Malformed OpenSSH private key'); + + pubPEM = genOpenSSLECDSAPub(oid, ecpub); + pubSSH = genOpenSSHECDSAPub(oid, ecpub); + privPEM = genOpenSSLECDSAPriv(oid, ecpub, ecpriv); + break; + default: + return new Error('Unsupported OpenSSH private key type: ' + type); + } + + var privComment = utils.readString(data, data._pos, 'utf8'); + if (privComment === false) + return new Error('Malformed OpenSSH private key'); + + keys.push( + new OpenSSH_Private(type, privComment, privPEM, pubPEM, pubSSH, algo, + decrypted) + ); + } + var cnt = 0; + for (i = data._pos; i < data.length; ++i) { + if (data[i] !== (++cnt % 255)) + return new Error('Malformed OpenSSH private key'); + } + + return keys; + } +})(); + + + +function OpenSSH_Old_Private(type, comment, privPEM, pubPEM, pubSSH, algo, decrypted) { + this.type = type; + this.comment = comment; + this[SYM_PRIV_PEM] = privPEM; + this[SYM_PUB_PEM] = pubPEM; + this[SYM_PUB_SSH] = pubSSH; + this[SYM_HASH_ALGO] = algo; + this[SYM_DECRYPTED] = decrypted; +} +OpenSSH_Old_Private.prototype = BaseKey; +(function() { + var regexp = /^-----BEGIN (RSA|DSA|EC) PRIVATE KEY-----(?:\r\n|\n)((?:[^:]+:\s*[\S].*(?:\r\n|\n))*)([\s\S]+)(?:\r\n|\n)-----END (RSA|DSA|EC) PRIVATE KEY-----$/; + OpenSSH_Old_Private.parse = function(str, passphrase) { + var m = regexp.exec(str); + if (m === null) + return null; + var privBlob = Buffer.from(m[3], 'base64'); + var headers = m[2]; + var decrypted = false; + if (headers !== undefined) { + // encrypted key + headers = headers.split(/\r\n|\n/g); + for (var i = 0; i < headers.length; ++i) { + var header = headers[i]; + var sepIdx = header.indexOf(':'); + if (header.slice(0, sepIdx) === 'DEK-Info') { + var val = header.slice(sepIdx + 2); + sepIdx = val.indexOf(','); + if (sepIdx === -1) + continue; + var cipherName = val.slice(0, sepIdx).toLowerCase(); + if (supportedOpenSSLCiphers.indexOf(cipherName) === -1) { + return new Error( + 'Cipher (' + + cipherName + + ') not supported for encrypted OpenSSH private key' + ); + } + var encInfo = CIPHER_INFO_OPENSSL[cipherName]; + if (!encInfo) { + return new Error( + 'Cipher (' + + cipherName + + ') not supported for encrypted OpenSSH private key' + ); + } + var cipherIV = Buffer.from(val.slice(sepIdx + 1), 'hex'); + if (cipherIV.length !== encInfo.ivLen) + return new Error('Malformed encrypted OpenSSH private key'); + if (!passphrase) { + return new Error( + 'Encrypted OpenSSH private key detected, but no passphrase given' + ); + } + var cipherKey = createHash('md5') + .update(passphrase) + .update(cipherIV.slice(0, 8)) + .digest(); + while (cipherKey.length < encInfo.keyLen) { + cipherKey = combineBuffers( + cipherKey, + (createHash('md5') + .update(cipherKey) + .update(passphrase) + .update(cipherIV) + .digest()).slice(0, 8) + ); + } + if (cipherKey.length > encInfo.keyLen) + cipherKey = cipherKey.slice(0, encInfo.keyLen); + try { + var decipher = createDecipheriv(cipherName, cipherKey, cipherIV); + decipher.setAutoPadding(false); + privBlob = combineBuffers(decipher.update(privBlob), + decipher.final()); + decrypted = true; + } catch (ex) { + return ex; + } + } + } + } + + var type; + var privPEM; + var pubPEM; + var pubSSH; + var algo; + var reader; + var errMsg = 'Malformed OpenSSH private key'; + if (decrypted) + errMsg += '. Bad passphrase?'; + switch (m[1]) { + case 'RSA': + type = 'ssh-rsa'; + privPEM = makePEM('RSA PRIVATE', privBlob); + try { + reader = new Ber.Reader(privBlob); + reader.readSequence(); + reader.readInt(); // skip version + var n = reader.readString(Ber.Integer, true); + if (n === null) + return new Error(errMsg); + var e = reader.readString(Ber.Integer, true); + if (e === null) + return new Error(errMsg); + pubPEM = genOpenSSLRSAPub(n, e); + pubSSH = genOpenSSHRSAPub(n, e); + } catch (ex) { + return new Error(errMsg); + } + algo = 'sha1'; + break; + case 'DSA': + type = 'ssh-dss'; + privPEM = makePEM('DSA PRIVATE', privBlob); + try { + reader = new Ber.Reader(privBlob); + reader.readSequence(); + reader.readInt(); // skip version + var p = reader.readString(Ber.Integer, true); + if (p === null) + return new Error(errMsg); + var q = reader.readString(Ber.Integer, true); + if (q === null) + return new Error(errMsg); + var g = reader.readString(Ber.Integer, true); + if (g === null) + return new Error(errMsg); + var y = reader.readString(Ber.Integer, true); + if (y === null) + return new Error(errMsg); + pubPEM = genOpenSSLDSAPub(p, q, g, y); + pubSSH = genOpenSSHDSAPub(p, q, g, y); + } catch (ex) { + return new Error(errMsg); + } + algo = 'sha1'; + break; + case 'EC': + var ecSSLName; + var ecPriv; + try { + reader = new Ber.Reader(privBlob); + reader.readSequence(); + reader.readInt(); // skip version + ecPriv = reader.readString(Ber.OctetString, true); + reader.readByte(); // Skip "complex" context type byte + var offset = reader.readLength(); // Skip context length + if (offset !== null) { + reader._offset = offset; + var oid = reader.readOID(); + if (oid === null) + return new Error(errMsg); + switch (oid) { + case '1.2.840.10045.3.1.7': + // prime256v1/secp256r1 + ecSSLName = 'prime256v1'; + type = 'ecdsa-sha2-nistp256'; + algo = 'sha256'; + break; + case '1.3.132.0.34': + // secp384r1 + ecSSLName = 'secp384r1'; + type = 'ecdsa-sha2-nistp384'; + algo = 'sha384'; + break; + case '1.3.132.0.35': + // secp521r1 + ecSSLName = 'secp521r1'; + type = 'ecdsa-sha2-nistp521'; + algo = 'sha512'; + break; + default: + return new Error('Unsupported private key EC OID: ' + oid); + } + } else { + return new Error(errMsg); + } + } catch (ex) { + return new Error(errMsg); + } + privPEM = makePEM('EC PRIVATE', privBlob); + var pubBlob = genOpenSSLECDSAPubFromPriv(ecSSLName, ecPriv); + pubPEM = genOpenSSLECDSAPub(oid, pubBlob); + pubSSH = genOpenSSHECDSAPub(oid, pubBlob); + break; + } + + return new OpenSSH_Old_Private(type, '', privPEM, pubPEM, pubSSH, algo, + decrypted); + }; +})(); + + + +function PPK_Private(type, comment, privPEM, pubPEM, pubSSH, algo, decrypted) { + this.type = type; + this.comment = comment; + this[SYM_PRIV_PEM] = privPEM; + this[SYM_PUB_PEM] = pubPEM; + this[SYM_PUB_SSH] = pubSSH; + this[SYM_HASH_ALGO] = algo; + this[SYM_DECRYPTED] = decrypted; +} +PPK_Private.prototype = BaseKey; +(function() { + var EMPTY_PASSPHRASE = Buffer.alloc(0); + var PPK_IV = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + var PPK_PP1 = Buffer.from([0, 0, 0, 0]); + var PPK_PP2 = Buffer.from([0, 0, 0, 1]); + var regexp = /^PuTTY-User-Key-File-2: (ssh-(?:rsa|dss))\r?\nEncryption: (aes256-cbc|none)\r?\nComment: ([^\r\n]*)\r?\nPublic-Lines: \d+\r?\n([\s\S]+?)\r?\nPrivate-Lines: \d+\r?\n([\s\S]+?)\r?\nPrivate-MAC: ([^\r\n]+)/; + PPK_Private.parse = function(str, passphrase) { + var m = regexp.exec(str); + if (m === null) + return null; + // m[1] = key type + // m[2] = encryption type + // m[3] = comment + // m[4] = base64-encoded public key data: + // for "ssh-rsa": + // string "ssh-rsa" + // mpint e (public exponent) + // mpint n (modulus) + // for "ssh-dss": + // string "ssh-dss" + // mpint p (modulus) + // mpint q (prime) + // mpint g (base number) + // mpint y (public key parameter: g^x mod p) + // m[5] = base64-encoded private key data: + // for "ssh-rsa": + // mpint d (private exponent) + // mpint p (prime 1) + // mpint q (prime 2) + // mpint iqmp ([inverse of q] mod p) + // for "ssh-dss": + // mpint x (private key parameter) + // m[6] = SHA1 HMAC over: + // string name of algorithm ("ssh-dss", "ssh-rsa") + // string encryption type + // string comment + // string public key data + // string private-plaintext (including the final padding) + var cipherName = m[2]; + var encrypted = (cipherName !== 'none'); + if (encrypted && !passphrase) { + return new Error( + 'Encrypted PPK private key detected, but no passphrase given' + ); + } + + var privBlob = Buffer.from(m[5], 'base64'); + + if (encrypted) { + var encInfo = CIPHER_INFO[cipherName]; + var cipherKey = combineBuffers( + createHash('sha1').update(PPK_PP1).update(passphrase).digest(), + createHash('sha1').update(PPK_PP2).update(passphrase).digest() + ); + if (cipherKey.length > encInfo.keyLen) + cipherKey = cipherKey.slice(0, encInfo.keyLen); + try { + var decipher = createDecipheriv(SSH_TO_OPENSSL[cipherName], + cipherKey, + PPK_IV); + decipher.setAutoPadding(false); + privBlob = combineBuffers(decipher.update(privBlob), + decipher.final()); + decrypted = true; + } catch (ex) { + return ex; + } + } + + var type = m[1]; + var comment = m[3]; + var pubBlob = Buffer.from(m[4], 'base64'); + + var mac = m[6]; + var typeLen = type.length; + var cipherNameLen = cipherName.length; + var commentLen = Buffer.byteLength(comment); + var pubLen = pubBlob.length; + var privLen = privBlob.length; + var macData = Buffer.allocUnsafe(4 + typeLen + + 4 + cipherNameLen + + 4 + commentLen + + 4 + pubLen + + 4 + privLen); + var p = 0; + + writeUInt32BE(macData, typeLen, p); + macData.write(type, p += 4, typeLen, 'ascii'); + writeUInt32BE(macData, cipherNameLen, p += typeLen); + macData.write(cipherName, p += 4, cipherNameLen, 'ascii'); + writeUInt32BE(macData, commentLen, p += cipherNameLen); + macData.write(comment, p += 4, commentLen, 'utf8'); + writeUInt32BE(macData, pubLen, p += commentLen); + pubBlob.copy(macData, p += 4); + writeUInt32BE(macData, privLen, p += pubLen); + privBlob.copy(macData, p + 4); + + if (!passphrase) + passphrase = EMPTY_PASSPHRASE; + + var calcMAC = createHmac('sha1', + createHash('sha1') + .update('putty-private-key-file-mac-key') + .update(passphrase) + .digest()) + .update(macData) + .digest('hex'); + + if (calcMAC !== mac) { + if (encrypted) { + return new Error( + 'PPK private key integrity check failed -- bad passphrase?' + ); + } else { + return new Error('PPK private key integrity check failed'); + } + } + + // avoid cyclic require by requiring on first use + if (!utils) + utils = __webpack_require__(780); + + var pubPEM; + var pubSSH; + var privPEM; + pubBlob._pos = 0; + skipFields(pubBlob, 1); // skip (duplicate) key type + switch (type) { + case 'ssh-rsa': + var e = utils.readString(pubBlob, pubBlob._pos); + if (e === false) + return new Error('Malformed PPK public key'); + var n = utils.readString(pubBlob, pubBlob._pos); + if (n === false) + return new Error('Malformed PPK public key'); + var d = utils.readString(privBlob, 0); + if (d === false) + return new Error('Malformed PPK private key'); + var p = utils.readString(privBlob, privBlob._pos); + if (p === false) + return new Error('Malformed PPK private key'); + var q = utils.readString(privBlob, privBlob._pos); + if (q === false) + return new Error('Malformed PPK private key'); + var iqmp = utils.readString(privBlob, privBlob._pos); + if (iqmp === false) + return new Error('Malformed PPK private key'); + pubPEM = genOpenSSLRSAPub(n, e); + pubSSH = genOpenSSHRSAPub(n, e); + privPEM = genOpenSSLRSAPriv(n, e, d, iqmp, p, q); + break; + case 'ssh-dss': + var p = utils.readString(pubBlob, pubBlob._pos); + if (p === false) + return new Error('Malformed PPK public key'); + var q = utils.readString(pubBlob, pubBlob._pos); + if (q === false) + return new Error('Malformed PPK public key'); + var g = utils.readString(pubBlob, pubBlob._pos); + if (g === false) + return new Error('Malformed PPK public key'); + var y = utils.readString(pubBlob, pubBlob._pos); + if (y === false) + return new Error('Malformed PPK public key'); + var x = utils.readString(privBlob, 0); + if (x === false) + return new Error('Malformed PPK private key'); + + pubPEM = genOpenSSLDSAPub(p, q, g, y); + pubSSH = genOpenSSHDSAPub(p, q, g, y); + privPEM = genOpenSSLDSAPriv(p, q, g, y, x); + break; + } + + return new PPK_Private(type, comment, privPEM, pubPEM, pubSSH, 'sha1', + encrypted); + }; +})(); + + +function parseDER(data, baseType, comment, fullType) { + // avoid cyclic require by requiring on first use + if (!utils) + utils = __webpack_require__(780); + + var algo; + var pubPEM = null; + var pubSSH = null; + switch (baseType) { + case 'ssh-rsa': + var e = utils.readString(data, data._pos); + if (e === false) + return new Error('Malformed OpenSSH public key'); + var n = utils.readString(data, data._pos); + if (n === false) + return new Error('Malformed OpenSSH public key'); + pubPEM = genOpenSSLRSAPub(n, e); + pubSSH = genOpenSSHRSAPub(n, e); + algo = 'sha1'; + break; + case 'ssh-dss': + var p = utils.readString(data, data._pos); + if (p === false) + return new Error('Malformed OpenSSH public key'); + var q = utils.readString(data, data._pos); + if (q === false) + return new Error('Malformed OpenSSH public key'); + var g = utils.readString(data, data._pos); + if (g === false) + return new Error('Malformed OpenSSH public key'); + var y = utils.readString(data, data._pos); + if (y === false) + return new Error('Malformed OpenSSH public key'); + pubPEM = genOpenSSLDSAPub(p, q, g, y); + pubSSH = genOpenSSHDSAPub(p, q, g, y); + algo = 'sha1'; + break; + case 'ssh-ed25519': + var edpub = utils.readString(data, data._pos); + if (edpub === false || edpub.length !== 32) + return new Error('Malformed OpenSSH public key'); + pubPEM = genOpenSSLEdPub(edpub); + pubSSH = genOpenSSHEdPub(edpub); + algo = null; + break; + case 'ecdsa-sha2-nistp256': + algo = 'sha256'; + oid = '1.2.840.10045.3.1.7'; + case 'ecdsa-sha2-nistp384': + if (algo === undefined) { + algo = 'sha384'; + oid = '1.3.132.0.34'; + } + case 'ecdsa-sha2-nistp521': + if (algo === undefined) { + algo = 'sha512'; + oid = '1.3.132.0.35'; + } + // TODO: validate curve name against type + if (!skipFields(data, 1)) // Skip curve name + return new Error('Malformed OpenSSH public key'); + var ecpub = utils.readString(data, data._pos); + if (ecpub === false) + return new Error('Malformed OpenSSH public key'); + pubPEM = genOpenSSLECDSAPub(oid, ecpub); + pubSSH = genOpenSSHECDSAPub(oid, ecpub); + break; + default: + return new Error('Unsupported OpenSSH public key type: ' + baseType); + } + + return new OpenSSH_Public(fullType, comment, pubPEM, pubSSH, algo); +} +function OpenSSH_Public(type, comment, pubPEM, pubSSH, algo) { + this.type = type; + this.comment = comment; + this[SYM_PRIV_PEM] = null; + this[SYM_PUB_PEM] = pubPEM; + this[SYM_PUB_SSH] = pubSSH; + this[SYM_HASH_ALGO] = algo; + this[SYM_DECRYPTED] = false; +} +OpenSSH_Public.prototype = BaseKey; +(function() { + var regexp; + if (EDDSA_SUPPORTED) + regexp = /^(((?:ssh-(?:rsa|dss|ed25519))|ecdsa-sha2-nistp(?:256|384|521))(?:-cert-v0[01]@openssh.com)?) ([A-Z0-9a-z\/+=]+)(?:$|\s+([\S].*)?)$/; + else + regexp = /^(((?:ssh-(?:rsa|dss))|ecdsa-sha2-nistp(?:256|384|521))(?:-cert-v0[01]@openssh.com)?) ([A-Z0-9a-z\/+=]+)(?:$|\s+([\S].*)?)$/; + OpenSSH_Public.parse = function(str) { + var m = regexp.exec(str); + if (m === null) + return null; + // m[1] = full type + // m[2] = base type + // m[3] = base64-encoded public key + // m[4] = comment + + // avoid cyclic require by requiring on first use + if (!utils) + utils = __webpack_require__(780); + + var fullType = m[1]; + var baseType = m[2]; + var data = Buffer.from(m[3], 'base64'); + var comment = (m[4] || ''); + + var type = utils.readString(data, data._pos, 'ascii'); + if (type === false || type.indexOf(baseType) !== 0) + return new Error('Malformed OpenSSH public key'); + + return parseDER(data, baseType, comment, fullType); + }; +})(); + + + +function RFC4716_Public(type, comment, pubPEM, pubSSH, algo) { + this.type = type; + this.comment = comment; + this[SYM_PRIV_PEM] = null; + this[SYM_PUB_PEM] = pubPEM; + this[SYM_PUB_SSH] = pubSSH; + this[SYM_HASH_ALGO] = algo; + this[SYM_DECRYPTED] = false; +} +RFC4716_Public.prototype = BaseKey; +(function() { + var regexp = /^---- BEGIN SSH2 PUBLIC KEY ----(?:\r\n|\n)((?:(?:[\x21-\x7E]+?):(?:(?:.*?\\\r?\n)*.*)(?:\r\n|\n))*)((?:[A-Z0-9a-z\/+=]+(?:\r\n|\n))+)---- END SSH2 PUBLIC KEY ----$/; + var RE_HEADER = /^([\x21-\x7E]+?):((?:.*?\\\r?\n)*.*)$/gm; + var RE_HEADER_ENDS = /\\\r?\n/g; + RFC4716_Public.parse = function(str) { + var m = regexp.exec(str); + if (m === null) + return null; + // m[1] = header(s) + // m[2] = base64-encoded public key + + var headers = m[1]; + var data = Buffer.from(m[2], 'base64'); + var comment = ''; + + if (headers !== undefined) { + while (m = RE_HEADER.exec(headers)) { + if (m[1].toLowerCase() === 'comment') { + comment = trimStart(m[2].replace(RE_HEADER_ENDS, '')); + if (comment.length > 1 + && comment.charCodeAt(0) === 34/*'"'*/ + && comment.charCodeAt(comment.length - 1) === 34/*'"'*/) { + comment = comment.slice(1, -1); + } + } + } + } + + // avoid cyclic require by requiring on first use + if (!utils) + utils = __webpack_require__(780); + + var type = utils.readString(data, 0, 'ascii'); + if (type === false) + return new Error('Malformed RFC4716 public key'); + + var pubPEM = null; + var pubSSH = null; + switch (type) { + case 'ssh-rsa': + var e = utils.readString(data, data._pos); + if (e === false) + return new Error('Malformed RFC4716 public key'); + var n = utils.readString(data, data._pos); + if (n === false) + return new Error('Malformed RFC4716 public key'); + pubPEM = genOpenSSLRSAPub(n, e); + pubSSH = genOpenSSHRSAPub(n, e); + break; + case 'ssh-dss': + var p = utils.readString(data, data._pos); + if (p === false) + return new Error('Malformed RFC4716 public key'); + var q = utils.readString(data, data._pos); + if (q === false) + return new Error('Malformed RFC4716 public key'); + var g = utils.readString(data, data._pos); + if (g === false) + return new Error('Malformed RFC4716 public key'); + var y = utils.readString(data, data._pos); + if (y === false) + return new Error('Malformed RFC4716 public key'); + pubPEM = genOpenSSLDSAPub(p, q, g, y); + pubSSH = genOpenSSHDSAPub(p, q, g, y); + break; + default: + return new Error('Malformed RFC4716 public key'); + } + + return new RFC4716_Public(type, comment, pubPEM, pubSSH, 'sha1'); + }; +})(); + + + +module.exports = { + parseDERKey: function parseDERKey(data, type) { + return parseDER(data, type, '', type); + }, + parseKey: function parseKey(data, passphrase) { + if (Buffer.isBuffer(data)) + data = data.toString('utf8').trim(); + else if (typeof data !== 'string') + return new Error('Key data must be a Buffer or string'); + else + data = data.trim(); + + // intentional != + if (passphrase != undefined) { + if (typeof passphrase === 'string') + passphrase = Buffer.from(passphrase); + else if (!Buffer.isBuffer(passphrase)) + return new Error('Passphrase must be a string or Buffer when supplied'); + } + + var ret; + + // Private keys + if ((ret = OpenSSH_Private.parse(data, passphrase)) !== null) + return ret; + if ((ret = OpenSSH_Old_Private.parse(data, passphrase)) !== null) + return ret; + if ((ret = PPK_Private.parse(data, passphrase)) !== null) + return ret; + + // Public keys + if ((ret = OpenSSH_Public.parse(data)) !== null) + return ret; + if ((ret = RFC4716_Public.parse(data)) !== null) + return ret; + + return new Error('Unsupported key format'); + } +} + + +/***/ }), + +/***/ 801: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +var i; +var keys; +var len; + +var crypto = __webpack_require__(417); +var eddsaSupported = (function() { + if (typeof crypto.sign === 'function' + && typeof crypto.verify === 'function') { + var key = '-----BEGIN PRIVATE KEY-----\r\nMC4CAQAwBQYDK2VwBCIEIHKj+sVa9WcD' + + '/q2DJUJaf43Kptc8xYuUQA4bOFj9vC8T\r\n-----END PRIVATE KEY-----'; + var data = Buffer.from('a'); + var sig; + var verified; + try { + sig = crypto.sign(null, data, key); + verified = crypto.verify(null, data, key, sig); + } catch (ex) {} + return (Buffer.isBuffer(sig) && sig.length === 64 && verified === true); + } + + return false; +})(); + +var curve25519Supported = (typeof crypto.diffieHellman === 'function' + && typeof crypto.generateKeyPairSync === 'function' + && typeof crypto.createPublicKey === 'function'); + +var MESSAGE = exports.MESSAGE = { + // Transport layer protocol -- generic (1-19) + DISCONNECT: 1, + IGNORE: 2, + UNIMPLEMENTED: 3, + DEBUG: 4, + SERVICE_REQUEST: 5, + SERVICE_ACCEPT: 6, + + // Transport layer protocol -- algorithm negotiation (20-29) + KEXINIT: 20, + NEWKEYS: 21, + + // Transport layer protocol -- key exchange method-specific (30-49) + + // User auth protocol -- generic (50-59) + USERAUTH_REQUEST: 50, + USERAUTH_FAILURE: 51, + USERAUTH_SUCCESS: 52, + USERAUTH_BANNER: 53, + + // User auth protocol -- user auth method-specific (60-79) + + // Connection protocol -- generic (80-89) + GLOBAL_REQUEST: 80, + REQUEST_SUCCESS: 81, + REQUEST_FAILURE: 82, + + // Connection protocol -- channel-related (90-127) + CHANNEL_OPEN: 90, + CHANNEL_OPEN_CONFIRMATION: 91, + CHANNEL_OPEN_FAILURE: 92, + CHANNEL_WINDOW_ADJUST: 93, + CHANNEL_DATA: 94, + CHANNEL_EXTENDED_DATA: 95, + CHANNEL_EOF: 96, + CHANNEL_CLOSE: 97, + CHANNEL_REQUEST: 98, + CHANNEL_SUCCESS: 99, + CHANNEL_FAILURE: 100 + + // Reserved for client protocols (128-191) + + // Local extensions (192-155) +}; +for (i = 0, keys = Object.keys(MESSAGE), len = keys.length; i < len; ++i) + MESSAGE[MESSAGE[keys[i]]] = keys[i]; +// context-specific message codes: +MESSAGE.KEXDH_INIT = 30; +MESSAGE.KEXDH_REPLY = 31; +MESSAGE.KEXDH_GEX_REQUEST = 34; +MESSAGE.KEXDH_GEX_GROUP = 31; +MESSAGE.KEXDH_GEX_INIT = 32; +MESSAGE.KEXDH_GEX_REPLY = 33; +MESSAGE.KEXECDH_INIT = 30; // included here for completeness +MESSAGE.KEXECDH_REPLY = 31; // included here for completeness +MESSAGE.USERAUTH_PASSWD_CHANGEREQ = 60; +MESSAGE.USERAUTH_PK_OK = 60; +MESSAGE.USERAUTH_INFO_REQUEST = 60; +MESSAGE.USERAUTH_INFO_RESPONSE = 61; + +var DYNAMIC_KEXDH_MESSAGE = exports.DYNAMIC_KEXDH_MESSAGE = {}; +DYNAMIC_KEXDH_MESSAGE[MESSAGE.KEXDH_GEX_GROUP] = 'KEXDH_GEX_GROUP'; +DYNAMIC_KEXDH_MESSAGE[MESSAGE.KEXDH_GEX_REPLY] = 'KEXDH_GEX_REPLY'; + +var KEXDH_MESSAGE = exports.KEXDH_MESSAGE = {}; +KEXDH_MESSAGE[MESSAGE.KEXDH_INIT] = 'KEXDH_INIT'; +KEXDH_MESSAGE[MESSAGE.KEXDH_REPLY] = 'KEXDH_REPLY'; + +var DISCONNECT_REASON = exports.DISCONNECT_REASON = { + HOST_NOT_ALLOWED_TO_CONNECT: 1, + PROTOCOL_ERROR: 2, + KEY_EXCHANGE_FAILED: 3, + RESERVED: 4, + MAC_ERROR: 5, + COMPRESSION_ERROR: 6, + SERVICE_NOT_AVAILABLE: 7, + PROTOCOL_VERSION_NOT_SUPPORTED: 8, + HOST_KEY_NOT_VERIFIABLE: 9, + CONNECTION_LOST: 10, + BY_APPLICATION: 11, + TOO_MANY_CONNECTIONS: 12, + AUTH_CANCELED_BY_USER: 13, + NO_MORE_AUTH_METHODS_AVAILABLE: 14, + ILLEGAL_USER_NAME: 15 +}; +for (i = 0, keys = Object.keys(DISCONNECT_REASON), len = keys.length; + i < len; + ++i) { + DISCONNECT_REASON[DISCONNECT_REASON[keys[i]]] = keys[i]; +} + +var CHANNEL_OPEN_FAILURE = exports.CHANNEL_OPEN_FAILURE = { + ADMINISTRATIVELY_PROHIBITED: 1, + CONNECT_FAILED: 2, + UNKNOWN_CHANNEL_TYPE: 3, + RESOURCE_SHORTAGE: 4 +}; +for (i = 0, keys = Object.keys(CHANNEL_OPEN_FAILURE), len = keys.length; + i < len; + ++i) { + CHANNEL_OPEN_FAILURE[CHANNEL_OPEN_FAILURE[keys[i]]] = keys[i]; +} + +var TERMINAL_MODE = exports.TERMINAL_MODE = { + TTY_OP_END: 0, // Indicates end of options. + VINTR: 1, // Interrupt character; 255 if none. Similarly for the + // other characters. Not all of these characters are + // supported on all systems. + VQUIT: 2, // The quit character (sends SIGQUIT signal on POSIX + // systems). + VERASE: 3, // Erase the character to left of the cursor. + VKILL: 4, // Kill the current input line. + VEOF: 5, // End-of-file character (sends EOF from the terminal). + VEOL: 6, // End-of-line character in addition to carriage return + // and/or linefeed. + VEOL2: 7, // Additional end-of-line character. + VSTART: 8, // Continues paused output (normally control-Q). + VSTOP: 9, // Pauses output (normally control-S). + VSUSP: 10, // Suspends the current program. + VDSUSP: 11, // Another suspend character. + VREPRINT: 12, // Reprints the current input line. + VWERASE: 13, // Erases a word left of cursor. + VLNEXT: 14, // Enter the next character typed literally, even if it + // is a special character + VFLUSH: 15, // Character to flush output. + VSWTCH: 16, // Switch to a different shell layer. + VSTATUS: 17, // Prints system status line (load, command, pid, etc). + VDISCARD: 18, // Toggles the flushing of terminal output. + IGNPAR: 30, // The ignore parity flag. The parameter SHOULD be 0 + // if this flag is FALSE, and 1 if it is TRUE. + PARMRK: 31, // Mark parity and framing errors. + INPCK: 32, // Enable checking of parity errors. + ISTRIP: 33, // Strip 8th bit off characters. + INLCR: 34, // Map NL into CR on input. + IGNCR: 35, // Ignore CR on input. + ICRNL: 36, // Map CR to NL on input. + IUCLC: 37, // Translate uppercase characters to lowercase. + IXON: 38, // Enable output flow control. + IXANY: 39, // Any char will restart after stop. + IXOFF: 40, // Enable input flow control. + IMAXBEL: 41, // Ring bell on input queue full. + ISIG: 50, // Enable signals INTR, QUIT, [D]SUSP. + ICANON: 51, // Canonicalize input lines. + XCASE: 52, // Enable input and output of uppercase characters by + // preceding their lowercase equivalents with "\". + ECHO: 53, // Enable echoing. + ECHOE: 54, // Visually erase chars. + ECHOK: 55, // Kill character discards current line. + ECHONL: 56, // Echo NL even if ECHO is off. + NOFLSH: 57, // Don't flush after interrupt. + TOSTOP: 58, // Stop background jobs from output. + IEXTEN: 59, // Enable extensions. + ECHOCTL: 60, // Echo control characters as ^(Char). + ECHOKE: 61, // Visual erase for line kill. + PENDIN: 62, // Retype pending input. + OPOST: 70, // Enable output processing. + OLCUC: 71, // Convert lowercase to uppercase. + ONLCR: 72, // Map NL to CR-NL. + OCRNL: 73, // Translate carriage return to newline (output). + ONOCR: 74, // Translate newline to carriage return-newline + // (output). + ONLRET: 75, // Newline performs a carriage return (output). + CS7: 90, // 7 bit mode. + CS8: 91, // 8 bit mode. + PARENB: 92, // Parity enable. + PARODD: 93, // Odd parity, else even. + TTY_OP_ISPEED: 128, // Specifies the input baud rate in bits per second. + TTY_OP_OSPEED: 129 // Specifies the output baud rate in bits per second. +}; +for (i = 0, keys = Object.keys(TERMINAL_MODE), len = keys.length; i < len; ++i) + TERMINAL_MODE[TERMINAL_MODE[keys[i]]] = keys[i]; + +var CHANNEL_EXTENDED_DATATYPE = exports.CHANNEL_EXTENDED_DATATYPE = { + STDERR: 1 +}; +for (i = 0, keys = Object.keys(CHANNEL_EXTENDED_DATATYPE), len = keys.length; + i < len; + ++i) { + CHANNEL_EXTENDED_DATATYPE[CHANNEL_EXTENDED_DATATYPE[keys[i]]] = keys[i]; +} + +exports.SIGNALS = ['ABRT', 'ALRM', 'FPE', 'HUP', 'ILL', 'INT', + 'QUIT', 'SEGV', 'TERM', 'USR1', 'USR2', 'KILL', + 'PIPE']; + +var DEFAULT_KEX = [ + // https://tools.ietf.org/html/rfc5656#section-10.1 + 'ecdh-sha2-nistp256', + 'ecdh-sha2-nistp384', + 'ecdh-sha2-nistp521', + + // https://tools.ietf.org/html/rfc4419#section-4 + 'diffie-hellman-group-exchange-sha256', + + 'diffie-hellman-group14-sha256', + 'diffie-hellman-group16-sha512', + 'diffie-hellman-group18-sha512', + + 'diffie-hellman-group14-sha1', // REQUIRED +]; +if (curve25519Supported) { + DEFAULT_KEX.unshift('curve25519-sha256'); + DEFAULT_KEX.unshift('curve25519-sha256@libssh.org'); +} +var SUPPORTED_KEX = [ + // https://tools.ietf.org/html/rfc4419#section-4 + 'diffie-hellman-group-exchange-sha1', + + 'diffie-hellman-group1-sha1' // REQUIRED +]; +var KEX_BUF = Buffer.from(DEFAULT_KEX.join(','), 'ascii'); +SUPPORTED_KEX = DEFAULT_KEX.concat(SUPPORTED_KEX); + +var DEFAULT_SERVER_HOST_KEY = [ + 'ecdsa-sha2-nistp256', + 'ecdsa-sha2-nistp384', + 'ecdsa-sha2-nistp521', + 'ssh-rsa', +]; +if (eddsaSupported) + DEFAULT_SERVER_HOST_KEY.unshift('ssh-ed25519'); +var SUPPORTED_SERVER_HOST_KEY = [ + 'ssh-dss' +]; +var SERVER_HOST_KEY_BUF = Buffer.from(DEFAULT_SERVER_HOST_KEY.join(','), + 'ascii'); +SUPPORTED_SERVER_HOST_KEY = DEFAULT_SERVER_HOST_KEY.concat( + SUPPORTED_SERVER_HOST_KEY +); + +var DEFAULT_CIPHER = [ + // http://tools.ietf.org/html/rfc4344#section-4 + 'aes128-ctr', + 'aes192-ctr', + 'aes256-ctr', + + // http://tools.ietf.org/html/rfc5647 + 'aes128-gcm', + 'aes128-gcm@openssh.com', + 'aes256-gcm', + 'aes256-gcm@openssh.com' +]; +var SUPPORTED_CIPHER = [ + 'aes256-cbc', + 'aes192-cbc', + 'aes128-cbc', + 'blowfish-cbc', + '3des-cbc', + + // http://tools.ietf.org/html/rfc4345#section-4: + 'arcfour256', + 'arcfour128', + + 'cast128-cbc', + 'arcfour' +]; +var CIPHER_BUF = Buffer.from(DEFAULT_CIPHER.join(','), 'ascii'); +SUPPORTED_CIPHER = DEFAULT_CIPHER.concat(SUPPORTED_CIPHER); + +var DEFAULT_HMAC = [ + 'hmac-sha2-256', + 'hmac-sha2-512', + 'hmac-sha1', +]; +var SUPPORTED_HMAC = [ + 'hmac-md5', + 'hmac-sha2-256-96', // first 96 bits of HMAC-SHA256 + 'hmac-sha2-512-96', // first 96 bits of HMAC-SHA512 + 'hmac-ripemd160', + 'hmac-sha1-96', // first 96 bits of HMAC-SHA1 + 'hmac-md5-96' // first 96 bits of HMAC-MD5 +]; +var HMAC_BUF = Buffer.from(DEFAULT_HMAC.join(','), 'ascii'); +SUPPORTED_HMAC = DEFAULT_HMAC.concat(SUPPORTED_HMAC); + +var DEFAULT_COMPRESS = [ + 'none', + 'zlib@openssh.com', // ZLIB (LZ77) compression, except + // compression/decompression does not start until after + // successful user authentication + 'zlib' // ZLIB (LZ77) compression +]; +var SUPPORTED_COMPRESS = []; +var COMPRESS_BUF = Buffer.from(DEFAULT_COMPRESS.join(','), 'ascii'); +SUPPORTED_COMPRESS = DEFAULT_COMPRESS.concat(SUPPORTED_COMPRESS); + +function makeCipherInfo(blockLen, keyLen, ivLen, authLen, discardLen, stream) { + return { + blockLen: blockLen, + keyLen: keyLen, + ivLen: ivLen === 0 ? blockLen : ivLen, + authLen: authLen, + discardLen: discardLen, + stream: stream, + }; +} +exports.CIPHER_INFO = { + 'aes128-gcm': makeCipherInfo(16, 16, 12, 16, 0, false), + 'aes256-gcm': makeCipherInfo(16, 32, 12, 16, 0, false), + 'aes128-gcm@openssh.com': makeCipherInfo(16, 16, 12, 16, 0, false), + 'aes256-gcm@openssh.com': makeCipherInfo(16, 32, 12, 16, 0, false), + + 'aes128-cbc': makeCipherInfo(16, 16, 0, 0, 0, false), + 'aes192-cbc': makeCipherInfo(16, 24, 0, 0, 0, false), + 'aes256-cbc': makeCipherInfo(16, 32, 0, 0, 0, false), + 'rijndael-cbc@lysator.liu.se': makeCipherInfo(16, 32, 0, 0, 0, false), + '3des-cbc': makeCipherInfo(8, 24, 0, 0, 0, false), + 'blowfish-cbc': makeCipherInfo(8, 16, 0, 0, 0, false), + 'idea-cbc': makeCipherInfo(8, 16, 0, 0, 0, false), + 'cast128-cbc': makeCipherInfo(8, 16, 0, 0, 0, false), + 'camellia128-cbc': makeCipherInfo(16, 16, 0, 0, 0, false), + 'camellia192-cbc': makeCipherInfo(16, 24, 0, 0, 0, false), + 'camellia256-cbc': makeCipherInfo(16, 32, 0, 0, 0, false), + 'camellia128-cbc@openssh.com': makeCipherInfo(16, 16, 0, 0, 0, false), + 'camellia192-cbc@openssh.com': makeCipherInfo(16, 24, 0, 0, 0, false), + 'camellia256-cbc@openssh.com': makeCipherInfo(16, 32, 0, 0, 0, false), + + 'aes128-ctr': makeCipherInfo(16, 16, 0, 0, 0, false), + 'aes192-ctr': makeCipherInfo(16, 24, 0, 0, 0, false), + 'aes256-ctr': makeCipherInfo(16, 32, 0, 0, 0, false), + '3des-ctr': makeCipherInfo(8, 24, 0, 0, 0, false), + 'blowfish-ctr': makeCipherInfo(8, 16, 0, 0, 0, false), + 'cast128-ctr': makeCipherInfo(8, 16, 0, 0, 0, false), + 'camellia128-ctr': makeCipherInfo(16, 16, 0, 0, 0, false), + 'camellia192-ctr': makeCipherInfo(16, 24, 0, 0, 0, false), + 'camellia256-ctr': makeCipherInfo(16, 32, 0, 0, 0, false), + 'camellia128-ctr@openssh.com': makeCipherInfo(16, 16, 0, 0, 0, false), + 'camellia192-ctr@openssh.com': makeCipherInfo(16, 24, 0, 0, 0, false), + 'camellia256-ctr@openssh.com': makeCipherInfo(16, 32, 0, 0, 0, false), + + /* The "arcfour128" algorithm is the RC4 cipher, as described in + [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream + generated by the cipher MUST be discarded, and the first byte of the + first encrypted packet MUST be encrypted using the 1537th byte of + keystream. + + -- http://tools.ietf.org/html/rfc4345#section-4 */ + 'arcfour': makeCipherInfo(8, 16, 0, 0, 1536, true), + 'arcfour128': makeCipherInfo(8, 16, 0, 0, 1536, true), + 'arcfour256': makeCipherInfo(8, 32, 0, 0, 1536, true), + 'arcfour512': makeCipherInfo(8, 64, 0, 0, 1536, true), +}; + +function makeHMACInfo(len, actualLen) { + return { len: len, actualLen: actualLen }; +} +exports.HMAC_INFO = { + 'hmac-md5': makeHMACInfo(16, 16), + 'hmac-md5-96': makeHMACInfo(16, 12), + 'hmac-ripemd160': makeHMACInfo(20, 20), + 'hmac-sha1': makeHMACInfo(20, 20), + 'hmac-sha1-96': makeHMACInfo(20, 12), + 'hmac-sha2-256': makeHMACInfo(32, 32), + 'hmac-sha2-256-96': makeHMACInfo(32, 12), + 'hmac-sha2-512': makeHMACInfo(64, 64), + 'hmac-sha2-512-96': makeHMACInfo(64, 12), +}; + +exports.ALGORITHMS = { + KEX: DEFAULT_KEX, + KEX_BUF: KEX_BUF, + SUPPORTED_KEX: SUPPORTED_KEX, + + SERVER_HOST_KEY: DEFAULT_SERVER_HOST_KEY, + SERVER_HOST_KEY_BUF: SERVER_HOST_KEY_BUF, + SUPPORTED_SERVER_HOST_KEY: SUPPORTED_SERVER_HOST_KEY, + + CIPHER: DEFAULT_CIPHER, + CIPHER_BUF: CIPHER_BUF, + SUPPORTED_CIPHER: SUPPORTED_CIPHER, + + HMAC: DEFAULT_HMAC, + HMAC_BUF: HMAC_BUF, + SUPPORTED_HMAC: SUPPORTED_HMAC, + + COMPRESS: DEFAULT_COMPRESS, + COMPRESS_BUF: COMPRESS_BUF, + SUPPORTED_COMPRESS: SUPPORTED_COMPRESS +}; +exports.SSH_TO_OPENSSL = { + // ECDH key exchange + 'ecdh-sha2-nistp256': 'prime256v1', // OpenSSL's name for 'secp256r1' + 'ecdh-sha2-nistp384': 'secp384r1', + 'ecdh-sha2-nistp521': 'secp521r1', + // Ciphers + 'aes128-gcm': 'aes-128-gcm', + 'aes256-gcm': 'aes-256-gcm', + 'aes128-gcm@openssh.com': 'aes-128-gcm', + 'aes256-gcm@openssh.com': 'aes-256-gcm', + '3des-cbc': 'des-ede3-cbc', + 'blowfish-cbc': 'bf-cbc', + 'aes256-cbc': 'aes-256-cbc', + 'aes192-cbc': 'aes-192-cbc', + 'aes128-cbc': 'aes-128-cbc', + 'idea-cbc': 'idea-cbc', + 'cast128-cbc': 'cast-cbc', + 'rijndael-cbc@lysator.liu.se': 'aes-256-cbc', + 'arcfour128': 'rc4', + 'arcfour256': 'rc4', + 'arcfour512': 'rc4', + 'arcfour': 'rc4', + 'camellia128-cbc': 'camellia-128-cbc', + 'camellia192-cbc': 'camellia-192-cbc', + 'camellia256-cbc': 'camellia-256-cbc', + 'camellia128-cbc@openssh.com': 'camellia-128-cbc', + 'camellia192-cbc@openssh.com': 'camellia-192-cbc', + 'camellia256-cbc@openssh.com': 'camellia-256-cbc', + '3des-ctr': 'des-ede3', + 'blowfish-ctr': 'bf-ecb', + 'aes256-ctr': 'aes-256-ctr', + 'aes192-ctr': 'aes-192-ctr', + 'aes128-ctr': 'aes-128-ctr', + 'cast128-ctr': 'cast5-ecb', + 'camellia128-ctr': 'camellia-128-ecb', + 'camellia192-ctr': 'camellia-192-ecb', + 'camellia256-ctr': 'camellia-256-ecb', + 'camellia128-ctr@openssh.com': 'camellia-128-ecb', + 'camellia192-ctr@openssh.com': 'camellia-192-ecb', + 'camellia256-ctr@openssh.com': 'camellia-256-ecb', + // HMAC + 'hmac-sha1-96': 'sha1', + 'hmac-sha1': 'sha1', + 'hmac-sha2-256': 'sha256', + 'hmac-sha2-256-96': 'sha256', + 'hmac-sha2-512': 'sha512', + 'hmac-sha2-512-96': 'sha512', + 'hmac-md5-96': 'md5', + 'hmac-md5': 'md5', + 'hmac-ripemd160': 'ripemd160' +}; + +var BUGS = exports.BUGS = { + BAD_DHGEX: 1, + OLD_EXIT: 2, + DYN_RPORT_BUG: 4 +}; + +exports.BUGGY_IMPLS = [ + [ 'Cisco-1.25', BUGS.BAD_DHGEX ], + [ /^[0-9.]+$/, BUGS.OLD_EXIT ], // old SSH.com implementations + [ /^OpenSSH_5\.\d+/, BUGS.DYN_RPORT_BUG ] +]; + +exports.EDDSA_SUPPORTED = eddsaSupported; +exports.CURVE25519_SUPPORTED = curve25519Supported; + + +/***/ }), + +/***/ 831: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// a duplex stream is just a stream that is both readable and writable. +// Since JS doesn't have multiple prototypal inheritance, this class +// prototypally inherits from Readable, and then parasitically from +// Writable. + +/**/ + +var objectKeys = Object.keys || function (obj) { + var keys = []; + + for (var key in obj) { + keys.push(key); + } + + return keys; +}; +/**/ + + +module.exports = Duplex; + +var Readable = __webpack_require__(226); + +var Writable = __webpack_require__(241); + +__webpack_require__(689)(Duplex, Readable); + +{ + // Allow the keys array to be GC'ed. + var keys = objectKeys(Writable.prototype); + + for (var v = 0; v < keys.length; v++) { + var method = keys[v]; + if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; + } +} + +function Duplex(options) { + if (!(this instanceof Duplex)) return new Duplex(options); + Readable.call(this, options); + Writable.call(this, options); + this.allowHalfOpen = true; + + if (options) { + if (options.readable === false) this.readable = false; + if (options.writable === false) this.writable = false; + + if (options.allowHalfOpen === false) { + this.allowHalfOpen = false; + this.once('end', onend); + } + } +} + +Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState.highWaterMark; + } +}); +Object.defineProperty(Duplex.prototype, 'writableBuffer', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState && this._writableState.getBuffer(); + } +}); +Object.defineProperty(Duplex.prototype, 'writableLength', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState.length; + } +}); // the no-half-open enforcer + +function onend() { + // If the writable side ended, then we're ok. + if (this._writableState.ended) return; // no more data can be written. + // But allow more writes to happen in this tick. + + process.nextTick(onEndNT, this); +} + +function onEndNT(self) { + self.end(); +} + +Object.defineProperty(Duplex.prototype, 'destroyed', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + if (this._readableState === undefined || this._writableState === undefined) { + return false; + } + + return this._readableState.destroyed && this._writableState.destroyed; + }, + set: function set(value) { + // we ignore the value if the stream + // has not been initialized yet + if (this._readableState === undefined || this._writableState === undefined) { + return; + } // backward compatibility, the user is explicitly + // managing destroyed + + + this._readableState.destroyed = value; + this._writableState.destroyed = value; + } +}); + +/***/ }), + +/***/ 835: +/***/ (function(module) { + +module.exports = require("url"); + +/***/ }), + +/***/ 858: +/***/ (function(module) { + +module.exports = eval("require")("supports-color"); + + +/***/ }), + +/***/ 867: +/***/ (function(module) { + +module.exports = require("tty"); + +/***/ }), + +/***/ 881: +/***/ (function(module) { + +module.exports = require("dns"); + +/***/ }), + +/***/ 882: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. + + +module.exports = PassThrough; + +var Transform = __webpack_require__(925); + +__webpack_require__(689)(PassThrough, Transform); + +function PassThrough(options) { + if (!(this instanceof PassThrough)) return new PassThrough(options); + Transform.call(this, options); +} + +PassThrough.prototype._transform = function (chunk, encoding, cb) { + cb(null, chunk); +}; + +/***/ }), + +/***/ 885: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var Client = __webpack_require__(597).Client, + http = __webpack_require__(605); + +module.exports = function(opt) { + var conn = new Client(); + var agent = new http.Agent(); + + agent.createConnection = function(options, fn) { + conn.on('ready', function() { + conn.exec('docker system dial-stdio', function(err, stream) { + if (err) { + conn.end(); + agent.destroy(); + return; + } + + fn(null, stream); + + stream.on('close', () => { + conn.end(); + agent.destroy(); + }); + }); + }).connect(opt); + + conn.on('end', () => agent.destroy()); + }; + + return agent; +}; + + +/***/ }), + +/***/ 895: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var fs = __webpack_require__(747); + +module.exports = function (filepath, split, encoding) { + split = typeof split !== 'undefined' ? split : "\n"; + encoding = typeof encoding !== 'undefined' ? encoding : "utf8"; + + var ca = []; + var chain = fs.readFileSync(filepath, encoding); + if(chain.indexOf("-END CERTIFICATE-") < 0 || chain.indexOf("-BEGIN CERTIFICATE-") < 0){ + throw Error("File does not contain 'BEGIN CERTIFICATE' or 'END CERTIFICATE'"); + } + chain = chain.split(split); + var cert = []; + var _i, _len; + for (_i = 0, _len = chain.length; _i < _len; _i++) { + var line = chain[_i]; + if (!(line.length !== 0)) { + continue; + } + cert.push(line); + if (line.match(/-END CERTIFICATE-/)) { + ca.push(cert.join(split)); + cert = []; + } + } + return ca; +} + + +/***/ }), + +/***/ 896: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var _require = __webpack_require__(293), + Buffer = _require.Buffer; + +var _require2 = __webpack_require__(669), + inspect = _require2.inspect; + +var custom = inspect && inspect.custom || 'inspect'; + +function copyBuffer(src, target, offset) { + Buffer.prototype.copy.call(src, target, offset); +} + +module.exports = +/*#__PURE__*/ +function () { + function BufferList() { + _classCallCheck(this, BufferList); + + this.head = null; + this.tail = null; + this.length = 0; + } + + _createClass(BufferList, [{ + key: "push", + value: function push(v) { + var entry = { + data: v, + next: null + }; + if (this.length > 0) this.tail.next = entry;else this.head = entry; + this.tail = entry; + ++this.length; + } + }, { + key: "unshift", + value: function unshift(v) { + var entry = { + data: v, + next: this.head + }; + if (this.length === 0) this.tail = entry; + this.head = entry; + ++this.length; + } + }, { + key: "shift", + value: function shift() { + if (this.length === 0) return; + var ret = this.head.data; + if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; + --this.length; + return ret; + } + }, { + key: "clear", + value: function clear() { + this.head = this.tail = null; + this.length = 0; + } + }, { + key: "join", + value: function join(s) { + if (this.length === 0) return ''; + var p = this.head; + var ret = '' + p.data; + + while (p = p.next) { + ret += s + p.data; + } + + return ret; + } + }, { + key: "concat", + value: function concat(n) { + if (this.length === 0) return Buffer.alloc(0); + var ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + + while (p) { + copyBuffer(p.data, ret, i); + i += p.data.length; + p = p.next; + } + + return ret; + } // Consumes a specified amount of bytes or characters from the buffered data. + + }, { + key: "consume", + value: function consume(n, hasStrings) { + var ret; + + if (n < this.head.data.length) { + // `slice` is the same for buffers and strings. + ret = this.head.data.slice(0, n); + this.head.data = this.head.data.slice(n); + } else if (n === this.head.data.length) { + // First chunk is a perfect match. + ret = this.shift(); + } else { + // Result spans more than one buffer. + ret = hasStrings ? this._getString(n) : this._getBuffer(n); + } + + return ret; + } + }, { + key: "first", + value: function first() { + return this.head.data; + } // Consumes a specified amount of characters from the buffered data. + + }, { + key: "_getString", + value: function _getString(n) { + var p = this.head; + var c = 1; + var ret = p.data; + n -= ret.length; + + while (p = p.next) { + var str = p.data; + var nb = n > str.length ? str.length : n; + if (nb === str.length) ret += str;else ret += str.slice(0, n); + n -= nb; + + if (n === 0) { + if (nb === str.length) { + ++c; + if (p.next) this.head = p.next;else this.head = this.tail = null; + } else { + this.head = p; + p.data = str.slice(nb); + } + + break; + } + + ++c; + } + + this.length -= c; + return ret; + } // Consumes a specified amount of bytes from the buffered data. + + }, { + key: "_getBuffer", + value: function _getBuffer(n) { + var ret = Buffer.allocUnsafe(n); + var p = this.head; + var c = 1; + p.data.copy(ret); + n -= p.data.length; + + while (p = p.next) { + var buf = p.data; + var nb = n > buf.length ? buf.length : n; + buf.copy(ret, ret.length - n, 0, nb); + n -= nb; + + if (n === 0) { + if (nb === buf.length) { + ++c; + if (p.next) this.head = p.next;else this.head = this.tail = null; + } else { + this.head = p; + p.data = buf.slice(nb); + } + + break; + } + + ++c; + } + + this.length -= c; + return ret; + } // Make sure the linked list only shows the minimal necessary information. + + }, { + key: custom, + value: function value(_, options) { + return inspect(this, _objectSpread({}, options, { + // Only inspect one level. + depth: 0, + // It should not recurse. + customInspect: false + })); + } + }]); + + return BufferList; +}(); + +/***/ }), + +/***/ 900: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(255); + +/** + * Represents an network + * @param {Object} modem docker-modem + * @param {String} id Network's id + */ +var Network = function(modem, id) { + this.modem = modem; + this.id = id; +}; + +Network.prototype[__webpack_require__(669).inspect.custom] = function() { return this; }; + +/** + * Inspect + * @param {Function} callback Callback, if specified Docker will be queried. + * @return {Object} Id only if callback isn't specified. + */ +Network.prototype.inspect = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var opts = { + path: '/networks/' + this.id + '?', + method: 'GET', + statusCodes: { + 200: true, + 404: 'no such network', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(opts, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(opts, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Removes the network + * @param {[Object]} opts Remove options (optional) + * @param {Function} callback Callback + */ +Network.prototype.remove = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/networks/' + this.id, + method: 'DELETE', + statusCodes: { + 200: true, + 204: true, + 404: 'no such network', + 409: 'conflict', + 500: 'server error' + }, + options: args.opts + }; + + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Connects a container to a network + * @param {[Object]} opts Connect options (optional) + * @param {Function} callback Callback + */ +Network.prototype.connect = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/networks/' + this.id + '/connect', + method: 'POST', + statusCodes: { + 200: true, + 201: true, + 404: 'network or container is not found', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + +/** + * Disconnects a container from a network + * @param {[Object]} opts Disconnect options (optional) + * @param {Function} callback Callback + */ +Network.prototype.disconnect = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/networks/' + this.id + '/disconnect', + method: 'POST', + statusCodes: { + 200: true, + 201: true, + 404: 'network or container is not found', + 500: 'server error' + }, + options: args.opts + }; + + if(args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + + + +module.exports = Network; + + +/***/ }), + +/***/ 917: +/***/ (function(module, __unusedexports, __webpack_require__) { + + +/** + * For Node.js, simply re-export the core `util.deprecate` function. + */ + +module.exports = __webpack_require__(669).deprecate; + + +/***/ }), + +/***/ 925: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. + + +module.exports = Transform; + +var _require$codes = __webpack_require__(563).codes, + ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, + ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, + ERR_TRANSFORM_ALREADY_TRANSFORMING = _require$codes.ERR_TRANSFORM_ALREADY_TRANSFORMING, + ERR_TRANSFORM_WITH_LENGTH_0 = _require$codes.ERR_TRANSFORM_WITH_LENGTH_0; + +var Duplex = __webpack_require__(831); + +__webpack_require__(689)(Transform, Duplex); + +function afterTransform(er, data) { + var ts = this._transformState; + ts.transforming = false; + var cb = ts.writecb; + + if (cb === null) { + return this.emit('error', new ERR_MULTIPLE_CALLBACK()); + } + + ts.writechunk = null; + ts.writecb = null; + if (data != null) // single equals check for both `null` and `undefined` + this.push(data); + cb(er); + var rs = this._readableState; + rs.reading = false; + + if (rs.needReadable || rs.length < rs.highWaterMark) { + this._read(rs.highWaterMark); + } +} + +function Transform(options) { + if (!(this instanceof Transform)) return new Transform(options); + Duplex.call(this, options); + this._transformState = { + afterTransform: afterTransform.bind(this), + needTransform: false, + transforming: false, + writecb: null, + writechunk: null, + writeencoding: null + }; // start out asking for a readable event once data is transformed. + + this._readableState.needReadable = true; // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + + this._readableState.sync = false; + + if (options) { + if (typeof options.transform === 'function') this._transform = options.transform; + if (typeof options.flush === 'function') this._flush = options.flush; + } // When the writable side finishes, then flush out anything remaining. + + + this.on('prefinish', prefinish); +} + +function prefinish() { + var _this = this; + + if (typeof this._flush === 'function' && !this._readableState.destroyed) { + this._flush(function (er, data) { + done(_this, er, data); + }); + } else { + done(this, null, null); + } +} + +Transform.prototype.push = function (chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); +}; // This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. + + +Transform.prototype._transform = function (chunk, encoding, cb) { + cb(new ERR_METHOD_NOT_IMPLEMENTED('_transform()')); +}; + +Transform.prototype._write = function (chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); + } +}; // Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. + + +Transform.prototype._read = function (n) { + var ts = this._transformState; + + if (ts.writechunk !== null && !ts.transforming) { + ts.transforming = true; + + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; + } +}; + +Transform.prototype._destroy = function (err, cb) { + Duplex.prototype._destroy.call(this, err, function (err2) { + cb(err2); + }); +}; + +function done(stream, er, data) { + if (er) return stream.emit('error', er); + if (data != null) // single equals check for both `null` and `undefined` + stream.push(data); // TODO(BridgeAR): Write a test for these two error cases + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + + if (stream._writableState.length) throw new ERR_TRANSFORM_WITH_LENGTH_0(); + if (stream._transformState.transforming) throw new ERR_TRANSFORM_ALREADY_TRANSFORMING(); + return stream.push(null); +} + +/***/ }), + +/***/ 941: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + +const fs = __webpack_require__(747) +const path = __webpack_require__(622) + +/* istanbul ignore next */ +const LCHOWN = fs.lchown ? 'lchown' : 'chown' +/* istanbul ignore next */ +const LCHOWNSYNC = fs.lchownSync ? 'lchownSync' : 'chownSync' + +/* istanbul ignore next */ +const needEISDIRHandled = fs.lchown && + !process.version.match(/v1[1-9]+\./) && + !process.version.match(/v10\.[6-9]/) + +const lchownSync = (path, uid, gid) => { + try { + return fs[LCHOWNSYNC](path, uid, gid) + } catch (er) { + if (er.code !== 'ENOENT') + throw er + } +} + +/* istanbul ignore next */ +const chownSync = (path, uid, gid) => { + try { + return fs.chownSync(path, uid, gid) + } catch (er) { + if (er.code !== 'ENOENT') + throw er + } +} + +/* istanbul ignore next */ +const handleEISDIR = + needEISDIRHandled ? (path, uid, gid, cb) => er => { + // Node prior to v10 had a very questionable implementation of + // fs.lchown, which would always try to call fs.open on a directory + // Fall back to fs.chown in those cases. + if (!er || er.code !== 'EISDIR') + cb(er) + else + fs.chown(path, uid, gid, cb) + } + : (_, __, ___, cb) => cb + +/* istanbul ignore next */ +const handleEISDirSync = + needEISDIRHandled ? (path, uid, gid) => { + try { + return lchownSync(path, uid, gid) + } catch (er) { + if (er.code !== 'EISDIR') + throw er + chownSync(path, uid, gid) + } + } + : (path, uid, gid) => lchownSync(path, uid, gid) + +// fs.readdir could only accept an options object as of node v6 +const nodeVersion = process.version +let readdir = (path, options, cb) => fs.readdir(path, options, cb) +let readdirSync = (path, options) => fs.readdirSync(path, options) +/* istanbul ignore next */ +if (/^v4\./.test(nodeVersion)) + readdir = (path, options, cb) => fs.readdir(path, cb) + +const chown = (cpath, uid, gid, cb) => { + fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, er => { + // Skip ENOENT error + cb(er && er.code !== 'ENOENT' ? er : null) + })) +} + +const chownrKid = (p, child, uid, gid, cb) => { + if (typeof child === 'string') + return fs.lstat(path.resolve(p, child), (er, stats) => { + // Skip ENOENT error + if (er) + return cb(er.code !== 'ENOENT' ? er : null) + stats.name = child + chownrKid(p, stats, uid, gid, cb) + }) + + if (child.isDirectory()) { + chownr(path.resolve(p, child.name), uid, gid, er => { + if (er) + return cb(er) + const cpath = path.resolve(p, child.name) + chown(cpath, uid, gid, cb) + }) + } else { + const cpath = path.resolve(p, child.name) + chown(cpath, uid, gid, cb) + } +} + + +const chownr = (p, uid, gid, cb) => { + readdir(p, { withFileTypes: true }, (er, children) => { + // any error other than ENOTDIR or ENOTSUP means it's not readable, + // or doesn't exist. give up. + if (er) { + if (er.code === 'ENOENT') + return cb() + else if (er.code !== 'ENOTDIR' && er.code !== 'ENOTSUP') + return cb(er) + } + if (er || !children.length) + return chown(p, uid, gid, cb) + + let len = children.length + let errState = null + const then = er => { + if (errState) + return + if (er) + return cb(errState = er) + if (-- len === 0) + return chown(p, uid, gid, cb) + } + + children.forEach(child => chownrKid(p, child, uid, gid, then)) + }) +} + +const chownrKidSync = (p, child, uid, gid) => { + if (typeof child === 'string') { + try { + const stats = fs.lstatSync(path.resolve(p, child)) + stats.name = child + child = stats + } catch (er) { + if (er.code === 'ENOENT') + return + else + throw er + } + } + + if (child.isDirectory()) + chownrSync(path.resolve(p, child.name), uid, gid) + + handleEISDirSync(path.resolve(p, child.name), uid, gid) +} + +const chownrSync = (p, uid, gid) => { + let children + try { + children = readdirSync(p, { withFileTypes: true }) + } catch (er) { + if (er.code === 'ENOENT') + return + else if (er.code === 'ENOTDIR' || er.code === 'ENOTSUP') + return handleEISDirSync(p, uid, gid) + else + throw er + } + + if (children && children.length) + children.forEach(child => chownrKidSync(p, child, uid, gid)) + + return handleEISDirSync(p, uid, gid) +} + +module.exports = chownr +chownr.sync = chownrSync + + +/***/ }), + +/***/ 954: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +const DuplexStream = __webpack_require__(574).Duplex +const inherits = __webpack_require__(689) +const BufferList = __webpack_require__(670) + +function BufferListStream (callback) { + if (!(this instanceof BufferListStream)) { + return new BufferListStream(callback) + } + + if (typeof callback === 'function') { + this._callback = callback + + const piper = function piper (err) { + if (this._callback) { + this._callback(err) + this._callback = null + } + }.bind(this) + + this.on('pipe', function onPipe (src) { + src.on('error', piper) + }) + this.on('unpipe', function onUnpipe (src) { + src.removeListener('error', piper) + }) + + callback = null + } + + BufferList._init.call(this, callback) + DuplexStream.call(this) +} + +inherits(BufferListStream, DuplexStream) +Object.assign(BufferListStream.prototype, BufferList.prototype) + +BufferListStream.prototype._new = function _new (callback) { + return new BufferListStream(callback) +} + +BufferListStream.prototype._write = function _write (buf, encoding, callback) { + this._appendBuffer(buf) + + if (typeof callback === 'function') { + callback() + } +} + +BufferListStream.prototype._read = function _read (size) { + if (!this.length) { + return this.push(null) + } + + size = Math.min(size, this.length) + this.push(this.slice(0, size)) + this.consume(size) +} + +BufferListStream.prototype.end = function end (chunk) { + DuplexStream.prototype.end.call(this, chunk) + + if (this._callback) { + this._callback(null, this.slice()) + this._callback = null + } +} + +BufferListStream.prototype._destroy = function _destroy (err, cb) { + this._bufs.length = 0 + this.length = 0 + cb(err) +} + +BufferListStream.prototype._isBufferList = function _isBufferList (b) { + return b instanceof BufferListStream || b instanceof BufferList || BufferListStream.isBufferList(b) +} + +BufferListStream.isBufferList = BufferList.isBufferList + +module.exports = BufferListStream +module.exports.BufferListStream = BufferListStream +module.exports.BufferList = BufferList + + +/***/ }), + +/***/ 965: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var EventEmitter = __webpack_require__(614).EventEmitter, + Modem = __webpack_require__(787), + tar = __webpack_require__(120), + zlib = __webpack_require__(761), + Container = __webpack_require__(360), + Image = __webpack_require__(588), + Volume = __webpack_require__(479), + Network = __webpack_require__(900), + Service = __webpack_require__(76), + Plugin = __webpack_require__(546), + Secret = __webpack_require__(681), + Config = __webpack_require__(180), + Task = __webpack_require__(627), + Node = __webpack_require__(107), + Exec = __webpack_require__(757), + util = __webpack_require__(255), + extend = util.extend; + +var Docker = function(opts) { + if (!(this instanceof Docker)) return new Docker(opts); + + var plibrary = global.Promise; + + if (opts && opts.Promise) { + plibrary = opts.Promise; + + if (Object.keys(opts).length === 1) { + opts = undefined; + } + } + + this.modem = new Modem(opts); + this.modem.Promise = plibrary; +}; + +/** + * Creates a new container + * @param {Object} opts Create options + * @param {Function} callback Callback + */ +Docker.prototype.createContainer = function(opts, callback) { + var self = this; + var optsf = { + path: '/containers/create?', + method: 'POST', + options: opts, + authconfig: opts.authconfig, + statusCodes: { + 200: true, // unofficial, but proxies may return it + 201: true, + 404: 'no such container', + 406: 'impossible to attach', + 500: 'server error' + } + }; + + delete opts.authconfig; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(self.getContainer(data.Id)); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return callback(err, data); + callback(err, self.getContainer(data.Id)); + }); + } +}; + +/** + * Creates a new image + * @param {Object} auth Authentication (optional) + * @param {Object} opts Create options + * @param {Function} callback Callback + */ +Docker.prototype.createImage = function(auth, opts, callback) { + var self = this; + if (!callback && typeof opts === 'function') { + callback = opts; + opts = auth; + auth = opts.authconfig || undefined; + } else if (!callback && !opts) { + opts = auth; + auth = opts.authconfig; + } + + var optsf = { + path: '/images/create?', + method: 'POST', + options: opts, + authconfig: auth, + isStream: true, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Load image + * @param {String} file File + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.loadImage = function(file, opts, callback) { + var self = this; + if (!callback && typeof opts === 'function') { + callback = opts; + opts = null; + } + + var optsf = { + path: '/images/load?', + method: 'POST', + options: opts, + file: file, + isStream: true, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Import image from a tar archive + * @param {String} file File + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.importImage = function(file, opts, callback) { + var self = this; + if (!callback && typeof opts === 'function') { + callback = opts; + opts = undefined; + } + + if (!opts) + opts = {}; + + opts.fromSrc = '-'; + + var optsf = { + path: '/images/create?', + method: 'POST', + options: opts, + file: file, + isStream: true, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Verifies auth + * @param {Object} opts Options + * @param {Function} callback Callback + */ +Docker.prototype.checkAuth = function(opts, callback) { + var self = this; + var optsf = { + path: '/auth', + method: 'POST', + options: opts, + statusCodes: { + 200: true, + 204: true, + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Builds an image + * @param {String} file File + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.buildImage = function(file, opts, callback) { + var self = this; + var content; + + if (!callback && typeof opts === 'function') { + callback = opts; + opts = null; + } + + function build(file) { + var optsf = { + path: '/build?', + method: 'POST', + file: file, + options: opts, + isStream: true, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (opts) { + if (opts.registryconfig) { + optsf.registryconfig = optsf.options.registryconfig; + delete optsf.options.registryconfig; + } + + //undocumented? + if (opts.authconfig) { + optsf.authconfig = optsf.options.authconfig; + delete optsf.options.authconfig; + } + } + + if (callback === undefined) { + return new self.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + self.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } + } + + if (file && file.context) { + var pack = tar.pack(file.context, { + entries: file.src + }); + return build(pack.pipe(zlib.createGzip())); + } else { + return build(file); + } +}; + +/** + * Fetches a Container by ID + * @param {String} id Container's ID + */ +Docker.prototype.getContainer = function(id) { + return new Container(this.modem, id); +}; + +/** + * Fetches an Image by name + * @param {String} name Image's name + */ +Docker.prototype.getImage = function(name) { + return new Image(this.modem, name); +}; + +/** + * Fetches a Volume by name + * @param {String} name Volume's name + */ +Docker.prototype.getVolume = function(name) { + return new Volume(this.modem, name); +}; + +/** + * Fetches a Plugin by name + * @param {String} name Volume's name + */ +Docker.prototype.getPlugin = function(name, remote) { + return new Plugin(this.modem, name, remote); +}; + +/** + * Fetches a Service by id + * @param {String} id Services's id + */ +Docker.prototype.getService = function(id) { + return new Service(this.modem, id); +}; + +/** + * Fetches a Task by id + * @param {String} id Task's id + */ +Docker.prototype.getTask = function(id) { + return new Task(this.modem, id); +}; + +/** + * Fetches Node by id + * @param {String} id Node's id + */ +Docker.prototype.getNode = function(id) { + return new Node(this.modem, id); +}; + +/** + * Fetches a Network by id + * @param {String} id network's id + */ +Docker.prototype.getNetwork = function(id) { + return new Network(this.modem, id); +}; + +/** + * Fetches a Secret by id + * @param {String} id network's id + */ +Docker.prototype.getSecret = function(id) { + return new Secret(this.modem, id); +}; + +/** + * Fetches a Config by id + * @param {String} id network's id + */ +Docker.prototype.getConfig = function(id) { + return new Config(this.modem, id); +}; + +/** + * Fetches an Exec instance by ID + * @param {String} id Exec instance's ID + */ +Docker.prototype.getExec = function(id) { + return new Exec(this.modem, id); +}; + +/** + * Lists containers + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.listContainers = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/containers/json?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 400: 'bad parameter', + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Lists images + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.listImages = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/images/json?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 400: 'bad parameter', + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Get images + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.getImages = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/images/get?', + method: 'GET', + options: args.opts, + isStream: true, + statusCodes: { + 200: true, + 400: 'bad parameter', + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Lists Services + * @param {Object} opts + * @param {Function} callback Callback + */ +Docker.prototype.listServices = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/services?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Lists Nodes + * @param {Object} opts + * @param {Function} callback Callback + */ +Docker.prototype.listNodes = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/nodes?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 400: 'bad parameter', + 404: 'no such node', + 500: 'server error', + 503: 'node is not part of a swarm', + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Lists Tasks + * @param {Object} opts + * @param {Function} callback Callback + */ +Docker.prototype.listTasks = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/tasks?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Creates a new secret + * @param {Object} opts Create options + * @param {Function} callback Callback + */ +Docker.prototype.createSecret = function(opts, callback) { + var args = util.processArgs(opts, callback); + var self = this; + var optsf = { + path: '/secrets/create?', + method: 'POST', + options: args.opts, + statusCodes: { + 200: true, // unofficial, but proxies may return it + 201: true, + 406: 'server error or node is not part of a swarm', + 409: 'name conflicts with an existing object', + 500: 'server error' + } + }; + + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(self.getSecret(data.ID)); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, self.getSecret(data.ID)); + }); + } +}; + + +/** + * Creates a new config + * @param {Object} opts Config options + * @param {Function} callback Callback + */ +Docker.prototype.createConfig = function(opts, callback) { + var args = util.processArgs(opts, callback); + var self = this; + var optsf = { + path: '/configs/create?', + method: 'POST', + options: args.opts, + statusCodes: { + 200: true, // unofficial, but proxies may return it + 201: true, + 406: 'server error or node is not part of a swarm', + 409: 'name conflicts with an existing object', + 500: 'server error' + } + }; + + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(self.getConfig(data.ID)); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, self.getConfig(data.ID)); + }); + } +}; + + +/** + * Lists secrets + * @param {Object} opts + * @param {Function} callback Callback + */ +Docker.prototype.listSecrets = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/secrets?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Lists configs + * @param {Object} opts + * @param {Function} callback Callback + */ +Docker.prototype.listConfigs = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/configs?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Creates a new plugin + * @param {Object} opts Create options + * @param {Function} callback Callback + */ +Docker.prototype.createPlugin = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + var optsf = { + path: '/plugins/create?', + method: 'POST', + options: args.opts, + statusCodes: { + 200: true, // unofficial, but proxies may return it + 204: true, + 500: 'server error' + } + }; + + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(self.getPlugin(args.opts.name)); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, self.getPlugin(args.opts.name)); + }); + } +}; + + +/** + * Lists plugins + * @param {Object} opts + * @param {Function} callback Callback + */ +Docker.prototype.listPlugins = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/plugins?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Prune images + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.pruneImages = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/images/prune?', + method: 'POST', + options: args.opts, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Prune builder + * @param {Function} callback Callback + */ +Docker.prototype.pruneBuilder = function(callback) { + var self = this; + + var optsf = { + path: '/build/prune', + method: 'POST', + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Prune containers + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.pruneContainers = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/containers/prune?', + method: 'POST', + options: args.opts, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Prune volumes + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.pruneVolumes = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/volumes/prune?', + method: 'POST', + options: args.opts, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Prune networks + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.pruneNetworks = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/networks/prune?', + method: 'POST', + options: args.opts, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + +/** + * Creates a new volume + * @param {Object} opts Create options + * @param {Function} callback Callback + */ +Docker.prototype.createVolume = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + var optsf = { + path: '/volumes/create?', + method: 'POST', + options: args.opts, + statusCodes: { + 200: true, // unofficial, but proxies may return it + 201: true, + 500: 'server error' + } + }; + + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(self.getVolume(data.Name)); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, self.getVolume(data.Name)); + }); + } +}; + +/** + * Creates a new service + * @param {Object} auth + * @param {Object} opts Create options + * @param {Function} callback Callback + */ +Docker.prototype.createService = function(auth, opts, callback) { + if (!callback && typeof opts === 'function') { + callback = opts; + opts = auth; + auth = opts.authconfig || undefined; + } else if (!opts && !callback) { + opts = auth; + } + + + var self = this; + var optsf = { + path: '/services/create', + method: 'POST', + options: opts, + authconfig: auth, + statusCodes: { + 200: true, + 201: true, + 500: 'server error' + } + }; + + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(self.getService(data.ID || data.Id)); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return callback(err, data); + callback(err, self.getService(data.ID || data.Id)); + }); + } +}; + +/** + * Lists volumes + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.listVolumes = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/volumes?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 400: 'bad parameter', + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Creates a new network + * @param {Object} opts Create options + * @param {Function} callback Callback + */ +Docker.prototype.createNetwork = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + var optsf = { + path: '/networks/create?', + method: 'POST', + options: args.opts, + statusCodes: { + 200: true, // unofficial, but proxies may return it + 201: true, + 404: 'driver not found', + 500: 'server error' + } + }; + + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(self.getNetwork(data.Id)); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + if (err) return args.callback(err, data); + args.callback(err, self.getNetwork(data.Id)); + }); + } +}; + +/** + * Lists networks + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + */ +Docker.prototype.listNetworks = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/networks?', + method: 'GET', + options: args.opts, + statusCodes: { + 200: true, + 400: 'bad parameter', + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Search images + * @param {Object} opts Options + * @param {Function} callback Callback + */ +Docker.prototype.searchImages = function(opts, callback) { + var self = this; + var optsf = { + path: '/images/search?', + method: 'GET', + options: opts, + authconfig: opts.authconfig, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Info + * @param {Function} callback Callback with info + */ +Docker.prototype.info = function(callback) { + var self = this; + var opts = { + path: '/info', + method: 'GET', + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(opts, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(opts, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Version + * @param {Function} callback Callback + */ +Docker.prototype.version = function(callback) { + var self = this; + var opts = { + path: '/version', + method: 'GET', + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(opts, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(opts, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Ping + * @param {Function} callback Callback + */ +Docker.prototype.ping = function(callback) { + var self = this; + var optsf = { + path: '/_ping', + method: 'GET', + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * SystemDf equivalent to system/df API Engine + * get usage data information + * @param {Function} callback Callback + */ +Docker.prototype.df = function(callback) { + var self = this; + var optsf = { + path: '/system/df', + method: 'GET', + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +/** + * Events + * @param {Object} opts Events options, like 'since' (optional) + * @param {Function} callback Callback + */ +Docker.prototype.getEvents = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/events?', + method: 'GET', + options: args.opts, + isStream: true, + statusCodes: { + 200: true, + 500: 'server error' + } + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Pull is a wrapper around createImage, parsing image's tags. + * @param {String} repoTag Repository tag + * @param {Object} opts Options (optional) + * @param {Function} callback Callback + * @param {Object} auth Authentication (optional) + * @return {Object} Image + */ +Docker.prototype.pull = function(repoTag, opts, callback, auth) { + var args = util.processArgs(opts, callback); + + var imageSrc = util.parseRepositoryTag(repoTag); + args.opts.fromImage = imageSrc.repository; + args.opts.tag = imageSrc.tag || 'latest'; + + var argsf = [args.opts, args.callback]; + if (auth) { + argsf = [auth, args.opts, args.callback]; + } + return this.createImage.apply(this, argsf); +}; + +/** + * Like run command from Docker's CLI + * @param {String} image Image name to be used. + * @param {Array} cmd Command to run in array format. + * @param {Object} streamo Output stream + * @param {Object} createOptions Container create options (optional) + * @param {Object} startOptions Container start options (optional) + * @param {Function} callback Callback + * @return {Object} EventEmitter + */ +Docker.prototype.run = function(image, cmd, streamo, createOptions, startOptions, callback) { + if (typeof arguments[arguments.length - 1] === 'function') { + return this.runCallback(image, cmd, streamo, createOptions, startOptions, callback); + } else { + return this.runPromise(image, cmd, streamo, createOptions, startOptions); + } +}; + + +Docker.prototype.runCallback = function(image, cmd, streamo, createOptions, startOptions, callback) { + if (!callback && typeof createOptions === 'function') { + callback = createOptions; + createOptions = {}; + startOptions = {}; + } else if (!callback && typeof startOptions === 'function') { + callback = startOptions; + startOptions = {}; + } + + var hub = new EventEmitter(); + + function handler(err, container) { + if (err) return callback(err, null, container); + + hub.emit('container', container); + + container.attach({ + stream: true, + stdout: true, + stderr: true + }, function handler(err, stream) { + if (err) return callback(err, null, container); + + hub.emit('stream', stream); + + if (streamo) { + if (streamo instanceof Array) { + stream.on('end', function() { + try { + streamo[0].end(); + } catch (e) {} + try { + streamo[1].end(); + } catch (e) {} + }); + container.modem.demuxStream(stream, streamo[0], streamo[1]); + } else { + stream.setEncoding('utf8'); + stream.pipe(streamo, { + end: true + }); + } + } + + container.start(startOptions, function(err, data) { + if (err) return callback(err, data, container); + hub.emit('start', container); + + container.wait(function(err, data) { + hub.emit('data', data); + callback(err, data, container); + }); + }); + }); + } + + var optsc = { + 'Hostname': '', + 'User': '', + 'AttachStdin': false, + 'AttachStdout': true, + 'AttachStderr': true, + 'Tty': true, + 'OpenStdin': false, + 'StdinOnce': false, + 'Env': null, + 'Cmd': cmd, + 'Image': image, + 'Volumes': {}, + 'VolumesFrom': [] + }; + + extend(optsc, createOptions); + + this.createContainer(optsc, handler); + + return hub; +}; + +Docker.prototype.runPromise = function(image, cmd, streamo, createOptions, startOptions) { + var self = this; + + createOptions = createOptions || {}; + startOptions = startOptions || {}; + + var optsc = { + 'Hostname': '', + 'User': '', + 'AttachStdin': false, + 'AttachStdout': true, + 'AttachStderr': true, + 'Tty': true, + 'OpenStdin': false, + 'StdinOnce': false, + 'Env': null, + 'Cmd': cmd, + 'Image': image, + 'Volumes': {}, + 'VolumesFrom': [] + }; + + extend(optsc, createOptions); + + var containero; + + return new this.modem.Promise(function(resolve, reject) { + self.createContainer(optsc).then(function(container) { + containero = container; + return container.attach({ + stream: true, + stdout: true, + stderr: true + }); + }).then(function(stream) { + if (streamo) { + if (streamo instanceof Array) { + stream.on('end', function() { + try { + streamo[0].end(); + } catch (e) {} + try { + streamo[1].end(); + } catch (e) {} + }); + containero.modem.demuxStream(stream, streamo[0], streamo[1]); + } else { + stream.setEncoding('utf8'); + stream.pipe(streamo, { + end: true + }); + } + } + return containero.start(startOptions); + }).then(function(data) { + return containero.wait(); + }).then(function(data) { + resolve([data, containero]); + }).catch(function(err) { + reject(err); + }); + }); +}; + +/** + * Init swarm. + * + * @param {object} opts + * @param {function} callback + */ +Docker.prototype.swarmInit = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/swarm/init', + method: 'POST', + statusCodes: { + 200: true, + 400: 'bad parameter', + 406: 'node is already part of a Swarm' + }, + options: args.opts + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Join swarm. + * + * @param {object} opts + * @param {function} callback + */ +Docker.prototype.swarmJoin = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/swarm/join', + method: 'POST', + statusCodes: { + 200: true, + 400: 'bad parameter', + 406: 'node is already part of a Swarm' + }, + options: args.opts + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Leave swarm. + * + * @param {object} opts + * @param {function} callback + */ +Docker.prototype.swarmLeave = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/swarm/leave?', + method: 'POST', + statusCodes: { + 200: true, + 406: 'node is not part of a Swarm' + }, + options: args.opts + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + +/** + * Update swarm. + * + * @param {object} opts + * @param {function} callback + */ +Docker.prototype.swarmUpdate = function(opts, callback) { + var self = this; + var args = util.processArgs(opts, callback); + + var optsf = { + path: '/swarm/update?', + method: 'POST', + statusCodes: { + 200: true, + 400: 'bad parameter', + 406: 'node is already part of a Swarm' + }, + options: args.opts + }; + + if (args.callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + args.callback(err, data); + }); + } +}; + + +/** + * Inspect a Swarm. + * Warning: This method is not documented in the API + * + * @param {Function} callback Callback + */ +Docker.prototype.swarmInspect = function(callback) { + var self = this; + var optsf = { + path: '/swarm', + method: 'GET', + statusCodes: { + 200: true, + 406: 'This node is not a swarm manager', + 500: 'server error' + } + }; + + if (callback === undefined) { + return new this.modem.Promise(function(resolve, reject) { + self.modem.dial(optsf, function(err, data) { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + } else { + this.modem.dial(optsf, function(err, data) { + callback(err, data); + }); + } +}; + +Docker.Container = Container; +Docker.Image = Image; +Docker.Volume = Volume; +Docker.Network = Network; +Docker.Service = Service; +Docker.Plugin = Plugin; +Docker.Secret = Secret; +Docker.Task = Task; +Docker.Node = Node; +Docker.Exec = Exec; + +module.exports = Docker; + + +/***/ }), + +/***/ 998: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2011 Mark Cavage All rights reserved. + +var assert = __webpack_require__(357); +var Buffer = __webpack_require__(215).Buffer; +var ASN1 = __webpack_require__(362); +var errors = __webpack_require__(584); + + +// --- Globals + +var newInvalidAsn1Error = errors.newInvalidAsn1Error; + +var DEFAULT_OPTS = { + size: 1024, + growthFactor: 8 +}; + + +// --- Helpers + +function merge(from, to) { + assert.ok(from); + assert.equal(typeof (from), 'object'); + assert.ok(to); + assert.equal(typeof (to), 'object'); + + var keys = Object.getOwnPropertyNames(from); + keys.forEach(function (key) { + if (to[key]) + return; + + var value = Object.getOwnPropertyDescriptor(from, key); + Object.defineProperty(to, key, value); + }); + + return to; +} + + + +// --- API + +function Writer(options) { + options = merge(DEFAULT_OPTS, options || {}); + + this._buf = Buffer.alloc(options.size || 1024); + this._size = this._buf.length; + this._offset = 0; + this._options = options; + + // A list of offsets in the buffer where we need to insert + // sequence tag/len pairs. + this._seq = []; +} + +Object.defineProperty(Writer.prototype, 'buffer', { + get: function () { + if (this._seq.length) + throw newInvalidAsn1Error(this._seq.length + ' unended sequence(s)'); + + return (this._buf.slice(0, this._offset)); + } +}); + +Writer.prototype.writeByte = function (b) { + if (typeof (b) !== 'number') + throw new TypeError('argument must be a Number'); + + this._ensure(1); + this._buf[this._offset++] = b; +}; + + +Writer.prototype.writeInt = function (i, tag) { + if (typeof (i) !== 'number') + throw new TypeError('argument must be a Number'); + if (typeof (tag) !== 'number') + tag = ASN1.Integer; + + var sz = 4; + + while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000 >> 0)) && + (sz > 1)) { + sz--; + i <<= 8; + } + + if (sz > 4) + throw newInvalidAsn1Error('BER ints cannot be > 0xffffffff'); + + this._ensure(2 + sz); + this._buf[this._offset++] = tag; + this._buf[this._offset++] = sz; + + while (sz-- > 0) { + this._buf[this._offset++] = ((i & 0xff000000) >>> 24); + i <<= 8; + } + +}; + + +Writer.prototype.writeNull = function () { + this.writeByte(ASN1.Null); + this.writeByte(0x00); +}; + + +Writer.prototype.writeEnumeration = function (i, tag) { + if (typeof (i) !== 'number') + throw new TypeError('argument must be a Number'); + if (typeof (tag) !== 'number') + tag = ASN1.Enumeration; + + return this.writeInt(i, tag); +}; + + +Writer.prototype.writeBoolean = function (b, tag) { + if (typeof (b) !== 'boolean') + throw new TypeError('argument must be a Boolean'); + if (typeof (tag) !== 'number') + tag = ASN1.Boolean; + + this._ensure(3); + this._buf[this._offset++] = tag; + this._buf[this._offset++] = 0x01; + this._buf[this._offset++] = b ? 0xff : 0x00; +}; + + +Writer.prototype.writeString = function (s, tag) { + if (typeof (s) !== 'string') + throw new TypeError('argument must be a string (was: ' + typeof (s) + ')'); + if (typeof (tag) !== 'number') + tag = ASN1.OctetString; + + var len = Buffer.byteLength(s); + this.writeByte(tag); + this.writeLength(len); + if (len) { + this._ensure(len); + this._buf.write(s, this._offset); + this._offset += len; + } +}; + + +Writer.prototype.writeBuffer = function (buf, tag) { + if (typeof (tag) !== 'number') + throw new TypeError('tag must be a number'); + if (!Buffer.isBuffer(buf)) + throw new TypeError('argument must be a buffer'); + + this.writeByte(tag); + this.writeLength(buf.length); + this._ensure(buf.length); + buf.copy(this._buf, this._offset, 0, buf.length); + this._offset += buf.length; +}; + + +Writer.prototype.writeStringArray = function (strings) { + if ((!strings instanceof Array)) + throw new TypeError('argument must be an Array[String]'); + + var self = this; + strings.forEach(function (s) { + self.writeString(s); + }); +}; + +// This is really to solve DER cases, but whatever for now +Writer.prototype.writeOID = function (s, tag) { + if (typeof (s) !== 'string') + throw new TypeError('argument must be a string'); + if (typeof (tag) !== 'number') + tag = ASN1.OID; + + if (!/^([0-9]+\.){3,}[0-9]+$/.test(s)) + throw new Error('argument is not a valid OID string'); + + function encodeOctet(bytes, octet) { + if (octet < 128) { + bytes.push(octet); + } else if (octet < 16384) { + bytes.push((octet >>> 7) | 0x80); + bytes.push(octet & 0x7F); + } else if (octet < 2097152) { + bytes.push((octet >>> 14) | 0x80); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } else if (octet < 268435456) { + bytes.push((octet >>> 21) | 0x80); + bytes.push(((octet >>> 14) | 0x80) & 0xFF); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } else { + bytes.push(((octet >>> 28) | 0x80) & 0xFF); + bytes.push(((octet >>> 21) | 0x80) & 0xFF); + bytes.push(((octet >>> 14) | 0x80) & 0xFF); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } + } + + var tmp = s.split('.'); + var bytes = []; + bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10)); + tmp.slice(2).forEach(function (b) { + encodeOctet(bytes, parseInt(b, 10)); + }); + + var self = this; + this._ensure(2 + bytes.length); + this.writeByte(tag); + this.writeLength(bytes.length); + bytes.forEach(function (b) { + self.writeByte(b); + }); +}; + + +Writer.prototype.writeLength = function (len) { + if (typeof (len) !== 'number') + throw new TypeError('argument must be a Number'); + + this._ensure(4); + + if (len <= 0x7f) { + this._buf[this._offset++] = len; + } else if (len <= 0xff) { + this._buf[this._offset++] = 0x81; + this._buf[this._offset++] = len; + } else if (len <= 0xffff) { + this._buf[this._offset++] = 0x82; + this._buf[this._offset++] = len >> 8; + this._buf[this._offset++] = len; + } else if (len <= 0xffffff) { + this._buf[this._offset++] = 0x83; + this._buf[this._offset++] = len >> 16; + this._buf[this._offset++] = len >> 8; + this._buf[this._offset++] = len; + } else { + throw newInvalidAsn1Error('Length too long (> 4 bytes)'); + } +}; + +Writer.prototype.startSequence = function (tag) { + if (typeof (tag) !== 'number') + tag = ASN1.Sequence | ASN1.Constructor; + + this.writeByte(tag); + this._seq.push(this._offset); + this._ensure(3); + this._offset += 3; +}; + + +Writer.prototype.endSequence = function () { + var seq = this._seq.pop(); + var start = seq + 3; + var len = this._offset - start; + + if (len <= 0x7f) { + this._shift(start, len, -2); + this._buf[seq] = len; + } else if (len <= 0xff) { + this._shift(start, len, -1); + this._buf[seq] = 0x81; + this._buf[seq + 1] = len; + } else if (len <= 0xffff) { + this._buf[seq] = 0x82; + this._buf[seq + 1] = len >> 8; + this._buf[seq + 2] = len; + } else if (len <= 0xffffff) { + this._shift(start, len, 1); + this._buf[seq] = 0x83; + this._buf[seq + 1] = len >> 16; + this._buf[seq + 2] = len >> 8; + this._buf[seq + 3] = len; + } else { + throw newInvalidAsn1Error('Sequence too long'); + } +}; + + +Writer.prototype._shift = function (start, len, shift) { + assert.ok(start !== undefined); + assert.ok(len !== undefined); + assert.ok(shift); + + this._buf.copy(this._buf, start + shift, start, start + len); + this._offset += shift; +}; + +Writer.prototype._ensure = function (len) { + assert.ok(len); + + if (this._size - this._offset < len) { + var sz = this._size * this._options.growthFactor; + if (sz - this._offset < len) + sz += len; + + var buf = Buffer.alloc(sz); + + this._buf.copy(buf, 0, 0, this._offset); + this._buf = buf; + this._size = sz; + } +}; + + + +// --- Exported API + +module.exports = Writer; + + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/dist/pagent.exe b/dist/pagent.exe new file mode 100644 index 0000000000000000000000000000000000000000..6e8a71ca581ecd0f3ad621efffc366f9f1edf9da GIT binary patch literal 50688 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 SqY^kOfuj;QDuMrK3H(1XaR1u? literal 0 HcmV?d00001 diff --git a/main.js b/main.js new file mode 100644 index 0000000..d07c433 --- /dev/null +++ b/main.js @@ -0,0 +1,48 @@ +const core = require('@actions/core'); +const fs = require('fs'); +const path = require('path') + +// Setup Docker +const Docker = require('dockerode'); +var docker = new Docker({socketPath: '/var/run/docker.sock'}); + +try { + + // Get inputs + var docker_image = core.getInput('containerImage'); + var work_dir = core.getInput('directory'); + + process.chdir(work_dir); + + // Pull docker image for building + console.log("Pulling build image..."); + docker.pull(docker_image, function(err, stream) + { + + docker.modem.followProgress(stream, onFinished, onProgress); + + // Wait to run build until after pull complete + function onFinished(err, output) + { + console.log("Starting image...") + docker.run(docker_image, ['godot', '-d', '-s', '--path /builder', 'addons/gut/gut_cmdln.gd'], process.stdout, + + // Mount working directory to `/builder` + { HostConfig: { Binds: [ process.cwd() + ":/builder" ] }}, + + function (err, data, container) { + + if(err) + { + console.log(err); + } + + }) + } + function onProgress(event) {} + + }); + +} catch (error) { + core.setFailed(error.message); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d9f2de9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,233 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@actions/core": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz", + "integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg==" + }, + "@zeit/ncc": { + "version": "0.22.3", + "resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.22.3.tgz", + "integrity": "sha512-jnCLpLXWuw/PAiJiVbLjA8WBC0IJQbFeUwF4I9M+23MvIxTxk5pD4Q8byQBSPmHQjz5aBoA7AKAElQxMpjrCLQ==" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bl": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", + "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "docker-modem": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-2.1.4.tgz", + "integrity": "sha512-vDTzZjjO1sXMY7m0xKjGdFMMZL7vIUerkC3G4l6rnrpOET2M6AOufM8ajmQoOB+6RfSn6I/dlikCUq/Y91Q1sQ==", + "requires": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^0.8.7" + } + }, + "dockerode": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.2.1.tgz", + "integrity": "sha512-XsSVB5Wu5HWMg1aelV5hFSqFJaKS5x1aiV/+sT7YOzOq1IRl49I/UwV8Pe4x6t0iF9kiGkWu5jwfvbkcFVupBw==", + "requires": { + "docker-modem": "^2.1.0", + "tar-fs": "~2.0.1" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=" + }, + "ssh2": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz", + "integrity": "sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==", + "requires": { + "ssh2-streams": "~0.4.10" + } + }, + "ssh2-streams": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz", + "integrity": "sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==", + "requires": { + "asn1": "~0.2.0", + "bcrypt-pbkdf": "^1.0.2", + "streamsearch": "~0.1.2" + } + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "tar-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", + "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, + "tar-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz", + "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==", + "requires": { + "bl": "^4.0.1", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e9b8360 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "run-gut-tests-action", + "version": "1.0.0", + "description": "", + "main": "dist/main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "package": "ncc build main.js -o dist" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/josephbmanley/run-gut-tests-action.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/josephbmanley/run-gut-tests-action/issues" + }, + "homepage": "https://github.com/josephbmanley/run-gut-tests-action#readme", + "dependencies": { + "@actions/core": "^1.2.4", + "@zeit/ncc": "^0.22.3", + "dockerode": "^3.2.1" + } + } \ No newline at end of file diff --git a/test_proj/.gutconfig.json b/test_proj/.gutconfig.json new file mode 100644 index 0000000..1f46b7d --- /dev/null +++ b/test_proj/.gutconfig.json @@ -0,0 +1,10 @@ +{ + "dirs":[ + "res://tests/" + ], + "include_subdirs":true, + "ignore_pause":true, + "log_level":2, + "should_exit":true, + "should_maximize":false +} \ No newline at end of file diff --git a/test_proj/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5 b/test_proj/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5 new file mode 100644 index 0000000..7b61c3e --- /dev/null +++ b/test_proj/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5 @@ -0,0 +1,3 @@ +source_md5="47313fa4c47a9963fddd764e1ec6e4a8" +dest_md5="2ded9e7f9060e2b530aab678b135fc5b" + diff --git a/test_proj/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex b/test_proj/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex new file mode 100644 index 0000000000000000000000000000000000000000..3ca6461f885d763a3f3c745fe6819ed84bfd666a GIT binary patch literal 3413 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 Ypu)>z_tVyY8PEy_Pgg&ebxsLQ0QjXj$^ZZW literal 0 HcmV?d00001 diff --git a/test_proj/Gut.tscn b/test_proj/Gut.tscn new file mode 100644 index 0000000..9d8c3c5 --- /dev/null +++ b/test_proj/Gut.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/gut/plugin_control.gd" type="Script" id=1] + +[node name="Gut" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +rect_min_size = Vector2( 740, 250 ) +script = ExtResource( 1 ) +_font_name = "AnonymousPro" +_font_size = 20 +_font_color = Color( 0.8, 0.8, 0.8, 1 ) +_background_color = Color( 0.15, 0.15, 0.15, 1 ) +_color_output = true +_select_script = "" +_tests_like = "" +_inner_class_name = "" +_run_on_load = true +_should_maximize = false +_should_print_to_console = true +_show_orphans = true +_log_level = 1 +_yield_between_tests = true +_disable_strict_datatype_checks = false +_test_prefix = "test_" +_file_prefix = "test_" +_file_extension = ".gd" +_inner_class_prefix = "Test" +_temp_directory = "user://gut_temp_directory" +_export_path = "" +_include_subdirectories = true +_directory1 = "res://tests" +_directory2 = "" +_directory3 = "" +_directory4 = "" +_directory5 = "" +_directory6 = "" +_double_strategy = 1 +_pre_run_script = "" +_post_run_script = "" diff --git a/test_proj/addons/gut/GutScene.gd b/test_proj/addons/gut/GutScene.gd new file mode 100644 index 0000000..9894c9c --- /dev/null +++ b/test_proj/addons/gut/GutScene.gd @@ -0,0 +1,432 @@ +extends Panel + +onready var _script_list = $ScriptsList +onready var _nav = { + prev = $Navigation/Previous, + next = $Navigation/Next, + run = $Navigation/Run, + current_script = $Navigation/CurrentScript, + run_single = $Navigation/RunSingleScript +} +onready var _progress = { + script = $ScriptProgress, + script_xy = $ScriptProgress/xy, + test = $TestProgress, + test_xy = $TestProgress/xy +} +onready var _summary = { + failing = $Summary/Failing, + passing = $Summary/Passing, + fail_count = 0, + pass_count = 0 +} + +onready var _extras = $ExtraOptions +onready var _ignore_pauses = $ExtraOptions/IgnorePause +onready var _continue_button = $Continue/Continue +onready var _text_box = $TextDisplay/RichTextLabel + +onready var _titlebar = { + bar = $TitleBar, + time = $TitleBar/Time, + label = $TitleBar/Title +} + +onready var _user_files = $UserFileViewer + +var _mouse = { + down = false, + in_title = false, + down_pos = null, + in_handle = false +} +var _is_running = false +var _start_time = 0.0 +var _time = 0.0 + +const DEFAULT_TITLE = 'Gut: The Godot Unit Testing tool.' +var _pre_maximize_rect = null +var _font_size = 20 + +signal end_pause +signal ignore_pause +signal log_level_changed +signal run_script +signal run_single_script + +func _ready(): + + if(Engine.editor_hint): + return + + _pre_maximize_rect = get_rect() + _hide_scripts() + _update_controls() + _nav.current_script.set_text("No scripts available") + set_title() + clear_summary() + _titlebar.time.set_text("Time 0.0") + + _extras.visible = false + update() + + set_font_size(_font_size) + set_font('CourierPrime') + + _user_files.set_position(Vector2(10, 30)) + +func elapsed_time_as_str(): + return str("%.1f" % (_time / 1000.0), 's') + +func _process(_delta): + if(_is_running): + _time = OS.get_ticks_msec() - _start_time + _titlebar.time.set_text(str('Time: ', elapsed_time_as_str())) + +func _draw(): # needs get_size() + # Draw the lines in the corner to show where you can + # drag to resize the dialog + var grab_margin = 3 + var line_space = 3 + var grab_line_color = Color(.4, .4, .4) + for i in range(1, 10): + var x = rect_size - Vector2(i * line_space, grab_margin) + var y = rect_size - Vector2(grab_margin, i * line_space) + draw_line(x, y, grab_line_color, 1, true) + +func _on_Maximize_draw(): + # draw the maximize square thing. + var btn = $TitleBar/Maximize + btn.set_text('') + var w = btn.get_size().x + var h = btn.get_size().y + btn.draw_rect(Rect2(0, 0, w, h), Color(0, 0, 0, 1)) + btn.draw_rect(Rect2(2, 4, w - 4, h - 6), Color(1,1,1,1)) + +func _on_ShowExtras_draw(): + var btn = $Continue/ShowExtras + btn.set_text('') + var start_x = 20 + var start_y = 15 + var pad = 5 + var color = Color(.1, .1, .1, 1) + var width = 2 + for i in range(3): + var y = start_y + pad * i + btn.draw_line(Vector2(start_x, y), Vector2(btn.get_size().x - start_x, y), color, width, true) + +# #################### +# GUI Events +# #################### +func _on_Run_pressed(): + _run_mode() + emit_signal('run_script', get_selected_index()) + +func _on_CurrentScript_pressed(): + _toggle_scripts() + +func _on_Previous_pressed(): + _select_script(get_selected_index() - 1) + +func _on_Next_pressed(): + _select_script(get_selected_index() + 1) + +func _on_LogLevelSlider_value_changed(_value): + emit_signal('log_level_changed', $LogLevelSlider.value) + +func _on_Continue_pressed(): + _continue_button.disabled = true + emit_signal('end_pause') + +func _on_IgnorePause_pressed(): + var checked = _ignore_pauses.is_pressed() + emit_signal('ignore_pause', checked) + if(checked): + emit_signal('end_pause') + _continue_button.disabled = true + +func _on_RunSingleScript_pressed(): + _run_mode() + emit_signal('run_single_script', get_selected_index()) + +func _on_ScriptsList_item_selected(index): + var tmr = $ScriptsList/DoubleClickTimer + if(!tmr.is_stopped()): + _run_mode() + emit_signal('run_single_script', get_selected_index()) + tmr.stop() + else: + tmr.start() + + _select_script(index) + +func _on_TitleBar_mouse_entered(): + _mouse.in_title = true + +func _on_TitleBar_mouse_exited(): + _mouse.in_title = false + +func _input(event): + if(event is InputEventMouseButton): + if(event.button_index == 1): + _mouse.down = event.pressed + if(_mouse.down): + _mouse.down_pos = event.position + + if(_mouse.in_title): + if(event is InputEventMouseMotion and _mouse.down): + set_position(get_position() + (event.position - _mouse.down_pos)) + _mouse.down_pos = event.position + _pre_maximize_rect = get_rect() + + if(_mouse.in_handle): + if(event is InputEventMouseMotion and _mouse.down): + var new_size = rect_size + event.position - _mouse.down_pos + var new_mouse_down_pos = event.position + rect_size = new_size + _mouse.down_pos = new_mouse_down_pos + _pre_maximize_rect = get_rect() + +func _on_ResizeHandle_mouse_entered(): + _mouse.in_handle = true + +func _on_ResizeHandle_mouse_exited(): + _mouse.in_handle = false + +func _on_RichTextLabel_gui_input(ev): + pass + # leaving this b/c it is wired up and might have to send + # more signals through + +func _on_Copy_pressed(): + OS.clipboard = _text_box.text + +func _on_ShowExtras_toggled(button_pressed): + _extras.visible = button_pressed + +func _on_Maximize_pressed(): + if(get_rect() == _pre_maximize_rect): + maximize() + else: + rect_size = _pre_maximize_rect.size + rect_position = _pre_maximize_rect.position +# #################### +# Private +# #################### +func _run_mode(is_running=true): + if(is_running): + _start_time = OS.get_ticks_msec() + _time = 0.0 + clear_summary() + _is_running = is_running + + _hide_scripts() + var ctrls = $Navigation.get_children() + for i in range(ctrls.size()): + ctrls[i].disabled = is_running + +func _select_script(index): + var text = _script_list.get_item_text(index) + var max_len = 50 + if(text.length() > max_len): + text = '...' + text.right(text.length() - (max_len - 5)) + $Navigation/CurrentScript.set_text(text) + _script_list.select(index) + _update_controls() + +func _toggle_scripts(): + if(_script_list.visible): + _hide_scripts() + else: + _show_scripts() + +func _show_scripts(): + _script_list.show() + +func _hide_scripts(): + _script_list.hide() + +func _update_controls(): + var is_empty = _script_list.get_selected_items().size() == 0 + if(is_empty): + _nav.next.disabled = true + _nav.prev.disabled = true + else: + var index = get_selected_index() + _nav.prev.disabled = index <= 0 + _nav.next.disabled = index >= _script_list.get_item_count() - 1 + + _nav.run.disabled = is_empty + _nav.current_script.disabled = is_empty + _nav.run_single.disabled = is_empty + +func _update_summary(): + if(!_summary): + return + + var total = _summary.fail_count + _summary.pass_count + $Summary.visible = !total == 0 + $Summary/AssertCount.text = str('Failures ', _summary.fail_count, '/', total) +# #################### +# Public +# #################### +func run_mode(is_running=true): + _run_mode(is_running) + +func set_scripts(scripts): + _script_list.clear() + for i in range(scripts.size()): + _script_list.add_item(scripts[i]) + _select_script(0) + _update_controls() + +func select_script(index): + _select_script(index) + +func get_selected_index(): + return _script_list.get_selected_items()[0] + +func get_log_level(): + return $LogLevelSlider.value + +func set_log_level(value): + var new_value = value + if(new_value == null): + new_value = 0 + $LogLevelSlider.value = new_value + +func set_ignore_pause(should): + _ignore_pauses.pressed = should + +func get_ignore_pause(): + return _ignore_pauses.pressed + +func get_text_box(): + # due to some timing issue, this cannot return _text_box but can return + # this. + return $TextDisplay/RichTextLabel + +func end_run(): + _run_mode(false) + _update_controls() + +func set_progress_script_max(value): + var max_val = max(value, 1) + _progress.script.set_max(max_val) + _progress.script_xy.set_text(str('0/', max_val)) + +func set_progress_script_value(value): + _progress.script.set_value(value) + var txt = str(value, '/', _progress.test.get_max()) + _progress.script_xy.set_text(txt) + +func set_progress_test_max(value): + var max_val = max(value, 1) + _progress.test.set_max(max_val) + _progress.test_xy.set_text(str('0/', max_val)) + +func set_progress_test_value(value): + _progress.test.set_value(value) + var txt = str(value, '/', _progress.test.get_max()) + _progress.test_xy.set_text(txt) + +func clear_progress(): + _progress.test.set_value(0) + _progress.script.set_value(0) + +func pause(): + _continue_button.disabled = false + +func set_title(title=null): + if(title == null): + $TitleBar/Title.set_text(DEFAULT_TITLE) + else: + $TitleBar/Title.set_text(title) + +func add_passing(amount=1): + if(!_summary): + return + _summary.pass_count += amount + _update_summary() + +func add_failing(amount=1): + if(!_summary): + return + _summary.fail_count += amount + _update_summary() + +func clear_summary(): + _summary.fail_count = 0 + _summary.pass_count = 0 + _update_summary() + +func maximize(): + if(is_inside_tree()): + var vp_size_offset = get_viewport().size + rect_size = vp_size_offset / get_scale() + set_position(Vector2(0, 0)) + +func clear_text(): + _text_box.bbcode_text = '' + +func scroll_to_bottom(): + pass + #_text_box.cursor_set_line(_gui.get_text_box().get_line_count()) + +func _set_font_size_for_rtl(rtl, new_size): + if(rtl.get('custom_fonts/normal_font') != null): + rtl.get('custom_fonts/bold_italics_font').size = new_size + rtl.get('custom_fonts/bold_font').size = new_size + rtl.get('custom_fonts/italics_font').size = new_size + rtl.get('custom_fonts/normal_font').size = new_size + + +func _set_fonts_for_rtl(rtl, base_font_name): + pass + + +func set_font_size(new_size): + _font_size = new_size + _set_font_size_for_rtl(_text_box, new_size) + _set_font_size_for_rtl(_user_files.get_rich_text_label(), new_size) + + +func _set_font(rtl, font_name, custom_name): + if(font_name == null): + rtl.set('custom_fonts/' + custom_name, null) + else: + var dyn_font = DynamicFont.new() + var font_data = DynamicFontData.new() + font_data.font_path = 'res://addons/gut/fonts/' + font_name + '.ttf' + font_data.antialiased = true + dyn_font.font_data = font_data + rtl.set('custom_fonts/' + custom_name, dyn_font) + +func _set_all_fonts_in_ftl(ftl, base_name): + if(base_name == 'Default'): + _set_font(ftl, null, 'normal_font') + _set_font(ftl, null, 'bold_font') + _set_font(ftl, null, 'italics_font') + _set_font(ftl, null, 'bold_italics_font') + else: + _set_font(ftl, base_name + '-Regular', 'normal_font') + _set_font(ftl, base_name + '-Bold', 'bold_font') + _set_font(ftl, base_name + '-Italic', 'italics_font') + _set_font(ftl, base_name + '-BoldItalic', 'bold_italics_font') + set_font_size(_font_size) + +func set_font(base_name): + _set_all_fonts_in_ftl(_text_box, base_name) + _set_all_fonts_in_ftl(_user_files.get_rich_text_label(), base_name) + +func set_default_font_color(color): + _text_box.set('custom_colors/default_color', color) + +func set_background_color(color): + $TextDisplay.color = color + +func _on_UserFiles_pressed(): + _user_files.show_open() + +func get_waiting_label(): + return $TextDisplay/WaitingLabel diff --git a/test_proj/addons/gut/GutScene.tscn b/test_proj/addons/gut/GutScene.tscn new file mode 100644 index 0000000..3819a6e --- /dev/null +++ b/test_proj/addons/gut/GutScene.tscn @@ -0,0 +1,471 @@ +[gd_scene load_steps=15 format=2] + +[ext_resource path="res://addons/gut/GutScene.gd" type="Script" id=1] +[ext_resource path="res://addons/gut/fonts/AnonymousPro-Italic.ttf" type="DynamicFontData" id=2] +[ext_resource path="res://addons/gut/fonts/AnonymousPro-Regular.ttf" type="DynamicFontData" id=3] +[ext_resource path="res://addons/gut/fonts/AnonymousPro-BoldItalic.ttf" type="DynamicFontData" id=4] +[ext_resource path="res://addons/gut/fonts/AnonymousPro-Bold.ttf" type="DynamicFontData" id=5] +[ext_resource path="res://addons/gut/UserFileViewer.tscn" type="PackedScene" id=6] + +[sub_resource type="StyleBoxFlat" id=1] +bg_color = Color( 0.192157, 0.192157, 0.227451, 1 ) +corner_radius_top_left = 10 +corner_radius_top_right = 10 + +[sub_resource type="StyleBoxFlat" id=2] +bg_color = Color( 1, 1, 1, 1 ) +border_color = Color( 0, 0, 0, 1 ) +corner_radius_top_left = 5 +corner_radius_top_right = 5 + +[sub_resource type="Theme" id=3] +resource_local_to_scene = true +Panel/styles/panel = SubResource( 2 ) +Panel/styles/panelf = null +Panel/styles/panelnc = null + +[sub_resource type="DynamicFont" id=4] +font_data = ExtResource( 4 ) + +[sub_resource type="DynamicFont" id=5] +font_data = ExtResource( 2 ) + +[sub_resource type="DynamicFont" id=6] +font_data = ExtResource( 5 ) + +[sub_resource type="DynamicFont" id=7] +font_data = ExtResource( 3 ) + +[sub_resource type="StyleBoxFlat" id=8] +bg_color = Color( 0.192157, 0.192157, 0.227451, 1 ) +corner_radius_top_left = 20 +corner_radius_top_right = 20 + +[node name="Gut" type="Panel"] +margin_right = 880.0 +margin_bottom = 360.0 +rect_min_size = Vector2( 740, 250 ) +custom_styles/panel = SubResource( 1 ) +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="UserFileViewer" parent="." instance=ExtResource( 6 )] +margin_top = 388.0 +margin_bottom = 818.0 + +[node name="TitleBar" type="Panel" parent="."] +anchor_top = -0.000491047 +anchor_right = 1.0 +anchor_bottom = -0.000491047 +margin_left = 1.0 +margin_top = 1.17678 +margin_right = -1.0 +margin_bottom = 40.1768 +theme = SubResource( 3 ) +__meta__ = { +"_edit_group_": true, +"_edit_use_anchors_": false +} + +[node name="Title" type="Label" parent="TitleBar"] +anchor_right = 1.0 +margin_bottom = 40.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "Gut" +align = 1 +valign = 1 + +[node name="Time" type="Label" parent="TitleBar"] +anchor_left = 1.0 +anchor_right = 1.0 +margin_left = -105.0 +margin_right = -53.0 +margin_bottom = 40.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "9999.99" +valign = 1 + +[node name="Maximize" type="Button" parent="TitleBar"] +anchor_left = 1.0 +anchor_right = 1.0 +margin_left = -30.0 +margin_top = 10.0 +margin_right = -6.0 +margin_bottom = 30.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "M" +flat = true + +[node name="ScriptProgress" type="ProgressBar" parent="."] +anchor_top = 1.0 +anchor_bottom = 1.0 +margin_left = 75.0 +margin_top = -70.0 +margin_right = 185.0 +margin_bottom = -40.0 +hint_tooltip = "Overall progress of executing tests." +step = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="ScriptProgress"] +margin_left = -70.0 +margin_right = -5.0 +margin_bottom = 30.0 +text = "Scripts" +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="xy" type="Label" parent="ScriptProgress"] +visible = false +margin_right = 110.0 +margin_bottom = 30.0 +text = "0/0" +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="TestProgress" type="ProgressBar" parent="."] +anchor_top = 1.0 +anchor_bottom = 1.0 +margin_left = 75.0 +margin_top = -105.0 +margin_right = 185.0 +margin_bottom = -75.0 +hint_tooltip = "Test progress for the current script." +step = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="TestProgress"] +margin_left = -70.0 +margin_right = -5.0 +margin_bottom = 30.0 +text = "Tests" +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="xy" type="Label" parent="TestProgress"] +visible = false +margin_right = 110.0 +margin_bottom = 30.0 +text = "0/0" +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="TextDisplay" type="ColorRect" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_top = 40.0 +margin_bottom = -110.0 +color = Color( 0, 0, 0, 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="RichTextLabel" type="RichTextLabel" parent="TextDisplay"] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 10.0 +focus_mode = 2 +custom_fonts/bold_italics_font = SubResource( 4 ) +custom_fonts/italics_font = SubResource( 5 ) +custom_fonts/bold_font = SubResource( 6 ) +custom_fonts/normal_font = SubResource( 7 ) +bbcode_enabled = true +scroll_following = true +selection_enabled = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="WaitingLabel" type="RichTextLabel" parent="TextDisplay"] +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_top = -25.0 +bbcode_enabled = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Navigation" type="Panel" parent="."] +self_modulate = Color( 1, 1, 1, 0 ) +anchor_top = 1.0 +anchor_bottom = 1.0 +margin_left = 220.0 +margin_top = -99.0 +margin_right = 580.0 +margin_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Previous" type="Button" parent="Navigation"] +margin_left = -20.0 +margin_top = 44.0 +margin_right = 65.0 +margin_bottom = 84.0 +hint_tooltip = "Previous script in the list." +text = "|<" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Next" type="Button" parent="Navigation"] +margin_left = 250.0 +margin_top = 44.0 +margin_right = 335.0 +margin_bottom = 84.0 +hint_tooltip = "Next script in the list. +" +text = ">|" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Run" type="Button" parent="Navigation"] +margin_left = 70.0 +margin_top = 44.0 +margin_right = 155.0 +margin_bottom = 84.0 +hint_tooltip = "Run the currently selected item and all after it." +text = ">" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CurrentScript" type="Button" parent="Navigation"] +anchor_top = -0.01 +anchor_bottom = -0.01 +margin_left = -20.0 +margin_top = -5.0 +margin_right = 335.0 +margin_bottom = 35.0 +hint_tooltip = "Select a script to run. You can run just this script, or this script and all scripts after using the run buttons." +text = "res://test/unit/test_gut.gd" +clip_text = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="RunSingleScript" type="Button" parent="Navigation"] +margin_left = 160.0 +margin_top = 44.0 +margin_right = 245.0 +margin_bottom = 84.0 +hint_tooltip = "Run the currently selected item. + +If the selected item has Inner Test Classes +then they will all be run. If the selected item +is an Inner Test Class then only it will be run." +text = "> (1)" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="LogLevelSlider" type="HSlider" parent="."] +anchor_top = 1.0 +anchor_bottom = 1.0 +margin_left = 80.0 +margin_top = -40.0 +margin_right = 130.0 +margin_bottom = -20.0 +rect_scale = Vector2( 2, 2 ) +max_value = 2.0 +tick_count = 3 +ticks_on_borders = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="LogLevelSlider"] +margin_left = -37.0 +margin_right = 28.0 +margin_bottom = 40.0 +rect_scale = Vector2( 0.5, 0.5 ) +text = "Log Level" +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ScriptsList" type="ItemList" parent="."] +anchor_bottom = 1.0 +margin_left = 179.0 +margin_top = 40.0 +margin_right = 619.0 +margin_bottom = -110.0 +allow_reselect = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="DoubleClickTimer" type="Timer" parent="ScriptsList"] +wait_time = 0.3 +one_shot = true + +[node name="ExtraOptions" type="Panel" parent="."] +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -212.0 +margin_top = -260.0 +margin_right = -2.0 +margin_bottom = -106.0 +custom_styles/panel = SubResource( 8 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="IgnorePause" type="CheckBox" parent="ExtraOptions"] +margin_left = 18.0 +margin_right = 136.0 +margin_bottom = 24.0 +rect_scale = Vector2( 1.5, 1.5 ) +hint_tooltip = "Ignore all calls to pause_before_teardown." +text = "Ignore Pauses" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Copy" type="Button" parent="ExtraOptions"] +margin_left = 15.0 +margin_top = 40.0 +margin_right = 195.0 +margin_bottom = 80.0 +hint_tooltip = "Copy all output to the clipboard." +text = "Copy to Clipboard" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="UserFiles" type="Button" parent="ExtraOptions"] +margin_left = 15.0 +margin_top = 90.0 +margin_right = 195.0 +margin_bottom = 130.0 +hint_tooltip = "Copy all output to the clipboard." +text = "View User Files" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ResizeHandle" type="Control" parent="."] +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -40.0 +margin_top = -40.0 + +[node name="Continue" type="Panel" parent="."] +self_modulate = Color( 1, 1, 1, 0 ) +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -150.0 +margin_top = -100.0 +margin_right = -30.0 +margin_bottom = -10.0 + +[node name="Continue" type="Button" parent="Continue"] +margin_left = -2.0 +margin_top = 45.0 +margin_right = 117.0 +margin_bottom = 85.0 +hint_tooltip = "When a pause_before_teardown is encountered this button will be enabled and must be pressed to continue running tests." +disabled = true +text = "Continue" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ShowExtras" type="Button" parent="Continue"] +anchor_left = -0.0166667 +anchor_right = -0.0166667 +margin_left = 50.0 +margin_top = -5.0 +margin_right = 120.0 +margin_bottom = 35.0 +rect_pivot_offset = Vector2( 35, 20 ) +hint_tooltip = "Show/hide additional options." +toggle_mode = true +text = "_" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Summary" type="Node2D" parent="."] +position = Vector2( 0, 3 ) + +[node name="Passing" type="Label" parent="Summary"] +visible = false +margin_left = 5.0 +margin_top = 7.0 +margin_right = 45.0 +margin_bottom = 21.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "0" +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Failing" type="Label" parent="Summary"] +visible = false +margin_left = 100.0 +margin_top = 7.0 +margin_right = 140.0 +margin_bottom = 21.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "0" +align = 1 +valign = 1 + +[node name="AssertCount" type="Label" parent="Summary"] +margin_left = 5.0 +margin_top = 7.0 +margin_right = 165.0 +margin_bottom = 21.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "Assert count" +__meta__ = { +"_edit_use_anchors_": false +} +[connection signal="mouse_entered" from="TitleBar" to="." method="_on_TitleBar_mouse_entered"] +[connection signal="mouse_exited" from="TitleBar" to="." method="_on_TitleBar_mouse_exited"] +[connection signal="draw" from="TitleBar/Maximize" to="." method="_on_Maximize_draw"] +[connection signal="pressed" from="TitleBar/Maximize" to="." method="_on_Maximize_pressed"] +[connection signal="gui_input" from="TextDisplay/RichTextLabel" to="." method="_on_RichTextLabel_gui_input"] +[connection signal="pressed" from="Navigation/Previous" to="." method="_on_Previous_pressed"] +[connection signal="pressed" from="Navigation/Next" to="." method="_on_Next_pressed"] +[connection signal="pressed" from="Navigation/Run" to="." method="_on_Run_pressed"] +[connection signal="pressed" from="Navigation/CurrentScript" to="." method="_on_CurrentScript_pressed"] +[connection signal="pressed" from="Navigation/RunSingleScript" to="." method="_on_RunSingleScript_pressed"] +[connection signal="value_changed" from="LogLevelSlider" to="." method="_on_LogLevelSlider_value_changed"] +[connection signal="item_selected" from="ScriptsList" to="." method="_on_ScriptsList_item_selected"] +[connection signal="pressed" from="ExtraOptions/IgnorePause" to="." method="_on_IgnorePause_pressed"] +[connection signal="pressed" from="ExtraOptions/Copy" to="." method="_on_Copy_pressed"] +[connection signal="pressed" from="ExtraOptions/UserFiles" to="." method="_on_UserFiles_pressed"] +[connection signal="mouse_entered" from="ResizeHandle" to="." method="_on_ResizeHandle_mouse_entered"] +[connection signal="mouse_exited" from="ResizeHandle" to="." method="_on_ResizeHandle_mouse_exited"] +[connection signal="pressed" from="Continue/Continue" to="." method="_on_Continue_pressed"] +[connection signal="draw" from="Continue/ShowExtras" to="." method="_on_ShowExtras_draw"] +[connection signal="toggled" from="Continue/ShowExtras" to="." method="_on_ShowExtras_toggled"] diff --git a/test_proj/addons/gut/LICENSE.md b/test_proj/addons/gut/LICENSE.md new file mode 100644 index 0000000..a38ac23 --- /dev/null +++ b/test_proj/addons/gut/LICENSE.md @@ -0,0 +1,22 @@ +The MIT License (MIT) +===================== + +Copyright (c) 2018 Tom "Butch" Wesley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/test_proj/addons/gut/UserFileViewer.gd b/test_proj/addons/gut/UserFileViewer.gd new file mode 100644 index 0000000..3984dec --- /dev/null +++ b/test_proj/addons/gut/UserFileViewer.gd @@ -0,0 +1,55 @@ +extends WindowDialog + +onready var rtl = $TextDisplay/RichTextLabel +var _has_opened_file = false + +func _get_file_as_text(path): + var to_return = null + var f = File.new() + var result = f.open(path, f.READ) + if(result == OK): + to_return = f.get_as_text() + f.close() + else: + to_return = str('ERROR: Could not open file. Error code ', result) + return to_return + +func _ready(): + rtl.clear() + +func _on_OpenFile_pressed(): + $FileDialog.popup_centered() + +func _on_FileDialog_file_selected(path): + show_file(path) + +func _on_Close_pressed(): + self.hide() + +func show_file(path): + var text = _get_file_as_text(path) + if(text == ''): + text = '' + rtl.set_text(text) + self.window_title = path + +func show_open(): + self.popup_centered() + $FileDialog.popup_centered() + +func _on_FileDialog_popup_hide(): + if(rtl.text.length() == 0): + self.hide() + +func get_rich_text_label(): + return $TextDisplay/RichTextLabel + +func _on_Home_pressed(): + rtl.scroll_to_line(0) + +func _on_End_pressed(): + rtl.scroll_to_line(rtl.get_line_count() -1) + + +func _on_Copy_pressed(): + OS.clipboard = rtl.text diff --git a/test_proj/addons/gut/UserFileViewer.tscn b/test_proj/addons/gut/UserFileViewer.tscn new file mode 100644 index 0000000..1236ebb --- /dev/null +++ b/test_proj/addons/gut/UserFileViewer.tscn @@ -0,0 +1,127 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/gut/UserFileViewer.gd" type="Script" id=1] + +[node name="UserFileViewer" type="WindowDialog"] +margin_top = 20.0 +margin_right = 800.0 +margin_bottom = 450.0 +rect_min_size = Vector2( 800, 180 ) +popup_exclusive = true +window_title = "View File" +resizable = true +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="FileDialog" type="FileDialog" parent="."] +margin_right = 416.0 +margin_bottom = 184.0 +rect_min_size = Vector2( 400, 140 ) +rect_scale = Vector2( 2, 2 ) +popup_exclusive = true +window_title = "Open a File" +resizable = true +mode = 0 +access = 1 +show_hidden_files = true +current_dir = "user://" +current_path = "user://" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="TextDisplay" type="ColorRect" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 8.0 +margin_right = -10.0 +margin_bottom = -65.0 +color = Color( 0.2, 0.188235, 0.188235, 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="RichTextLabel" type="RichTextLabel" parent="TextDisplay"] +anchor_right = 1.0 +anchor_bottom = 1.0 +focus_mode = 2 +text = "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content. Lorem ipsum may be used before final copy is available, but it may also be used to temporarily replace copy in a process called greeking, which allows designers to consider form without the meaning of the text influencing the design. + +Lorem ipsum is typically a corrupted version of De finibus bonorum et malorum, a first-century BCE text by the Roman statesman and philosopher Cicero, with words altered, added, and removed to make it nonsensical, improper Latin. + +Versions of the Lorem ipsum text have been used in typesetting at least since the 1960s, when it was popularized by advertisements for Letraset transfer sheets. Lorem ipsum was introduced to the digital world in the mid-1980s when Aldus employed it in graphic and word-processing templates for its desktop publishing program PageMaker. Other popular word processors including Pages and Microsoft Word have since adopted Lorem ipsum as well." +selection_enabled = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="OpenFile" type="Button" parent="."] +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -158.0 +margin_top = -50.0 +margin_right = -84.0 +margin_bottom = -30.0 +rect_scale = Vector2( 2, 2 ) +text = "Open File" + +[node name="Home" type="Button" parent="."] +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -478.0 +margin_top = -50.0 +margin_right = -404.0 +margin_bottom = -30.0 +rect_scale = Vector2( 2, 2 ) +text = "Home" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Copy" type="Button" parent="."] +anchor_top = 1.0 +anchor_bottom = 1.0 +margin_left = 160.0 +margin_top = -50.0 +margin_right = 234.0 +margin_bottom = -30.0 +rect_scale = Vector2( 2, 2 ) +text = "Copy" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="End" type="Button" parent="."] +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -318.0 +margin_top = -50.0 +margin_right = -244.0 +margin_bottom = -30.0 +rect_scale = Vector2( 2, 2 ) +text = "End" + +[node name="Close" type="Button" parent="."] +anchor_top = 1.0 +anchor_bottom = 1.0 +margin_left = 10.0 +margin_top = -50.0 +margin_right = 80.0 +margin_bottom = -30.0 +rect_scale = Vector2( 2, 2 ) +text = "Close" +[connection signal="file_selected" from="FileDialog" to="." method="_on_FileDialog_file_selected"] +[connection signal="popup_hide" from="FileDialog" to="." method="_on_FileDialog_popup_hide"] +[connection signal="pressed" from="OpenFile" to="." method="_on_OpenFile_pressed"] +[connection signal="pressed" from="Home" to="." method="_on_Home_pressed"] +[connection signal="pressed" from="Copy" to="." method="_on_Copy_pressed"] +[connection signal="pressed" from="End" to="." method="_on_End_pressed"] +[connection signal="pressed" from="Close" to="." method="_on_Close_pressed"] diff --git a/test_proj/addons/gut/autofree.gd b/test_proj/addons/gut/autofree.gd new file mode 100644 index 0000000..80b4e89 --- /dev/null +++ b/test_proj/addons/gut/autofree.gd @@ -0,0 +1,59 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# Class used to keep track of objects to be freed and utilities to free them. +# ############################################################################## +var _to_free = [] +var _to_queue_free = [] + +func add_free(thing): + if(typeof(thing) == TYPE_OBJECT): + if(!thing is Reference): + _to_free.append(thing) + +func add_queue_free(thing): + _to_queue_free.append(thing) + +func get_queue_free_count(): + return _to_queue_free.size() + +func get_free_count(): + return _to_free.size() + +func free_all(): + for i in range(_to_free.size()): + if(is_instance_valid(_to_free[i])): + _to_free[i].free() + _to_free.clear() + + for i in range(_to_queue_free.size()): + if(is_instance_valid(_to_queue_free[i])): + _to_queue_free[i].queue_free() + _to_queue_free.clear() + + diff --git a/test_proj/addons/gut/double_templates/function_template.txt b/test_proj/addons/gut/double_templates/function_template.txt new file mode 100644 index 0000000..666952e --- /dev/null +++ b/test_proj/addons/gut/double_templates/function_template.txt @@ -0,0 +1,6 @@ +{func_decleration} + __gut_spy('{method_name}', {param_array}) + if(__gut_should_call_super('{method_name}', {param_array})): + return {super_call} + else: + return __gut_get_stubbed_return('{method_name}', {param_array}) diff --git a/test_proj/addons/gut/double_templates/script_template.txt b/test_proj/addons/gut/double_templates/script_template.txt new file mode 100644 index 0000000..6fc7165 --- /dev/null +++ b/test_proj/addons/gut/double_templates/script_template.txt @@ -0,0 +1,41 @@ +{extends} + +var __gut_metadata_ = { + path = '{path}', + subpath = '{subpath}', + stubber = __gut_instance_from_id({stubber_id}), + spy = __gut_instance_from_id({spy_id}), + gut = __gut_instance_from_id({gut_id}), +} + +func __gut_instance_from_id(inst_id): + if(inst_id == -1): + return null + else: + return instance_from_id(inst_id) + +func __gut_should_call_super(method_name, called_with): + if(__gut_metadata_.stubber != null): + return __gut_metadata_.stubber.should_call_super(self, method_name, called_with) + else: + return false + +var __gut_utils_ = load('res://addons/gut/utils.gd').get_instance() + +func __gut_spy(method_name, called_with): + if(__gut_metadata_.spy != null): + __gut_metadata_.spy.add_call(self, method_name, called_with) + +func __gut_get_stubbed_return(method_name, called_with): + if(__gut_metadata_.stubber != null): + return __gut_metadata_.stubber.get_return(self, method_name, called_with) + else: + return null + +func _init(): + if(__gut_metadata_.gut != null): + __gut_metadata_.gut.get_autofree().add_free(self) + +# ------------------------------------------------------------------------------ +# Methods start here +# ------------------------------------------------------------------------------ diff --git a/test_proj/addons/gut/doubler.gd b/test_proj/addons/gut/doubler.gd new file mode 100644 index 0000000..c5e9e0e --- /dev/null +++ b/test_proj/addons/gut/doubler.gd @@ -0,0 +1,556 @@ +# ------------------------------------------------------------------------------ +# Utility class to hold the local and built in methods separately. Add all local +# methods FIRST, then add built ins. +# ------------------------------------------------------------------------------ +class ScriptMethods: + # List of methods that should not be overloaded when they are not defined + # in the class being doubled. These either break things if they are + # overloaded or do not have a "super" equivalent so we can't just pass + # through. + var _blacklist = [ + 'has_method', + 'get_script', + 'get', + '_notification', + 'get_path', + '_enter_tree', + '_exit_tree', + '_process', + '_draw', + '_physics_process', + '_input', + '_unhandled_input', + '_unhandled_key_input', + '_set', + '_get', # probably + 'emit_signal', # can't handle extra parameters to be sent with signal. + 'draw_mesh', # issue with one parameter, value is `Null((..), (..), (..))`` + '_to_string', # nonexistant function ._to_string + '_get_minimum_size', # Nonexistent function _get_minimum_size + ] + + # These methods should not be included in the double. + var _skip = [ + # There is an init in the template. There is also no real reason + # to include this method since it will always be called, it has no + # return value, and you cannot prevent super from being called. + '_init' + ] + + var built_ins = [] + var local_methods = [] + var _method_names = [] + + func is_blacklisted(method_meta): + return _blacklist.find(method_meta.name) != -1 + + func _add_name_if_does_not_have(method_name): + if(_skip.has(method_name)): + return false + var should_add = _method_names.find(method_name) == -1 + if(should_add): + _method_names.append(method_name) + return should_add + + func add_built_in_method(method_meta): + var did_add = _add_name_if_does_not_have(method_meta.name) + if(did_add and !is_blacklisted(method_meta)): + built_ins.append(method_meta) + + func add_local_method(method_meta): + var did_add = _add_name_if_does_not_have(method_meta.name) + if(did_add): + local_methods.append(method_meta) + + func to_s(): + var text = "Locals\n" + for i in range(local_methods.size()): + text += str(" ", local_methods[i].name, "\n") + text += "Built-Ins\n" + for i in range(built_ins.size()): + text += str(" ", built_ins[i].name, "\n") + return text + +# ------------------------------------------------------------------------------ +# Helper class to deal with objects and inner classes. +# ------------------------------------------------------------------------------ +class ObjectInfo: + var _path = null + var _subpaths = [] + var _utils = load('res://addons/gut/utils.gd').get_instance() + var _method_strategy = null + var make_partial_double = false + var scene_path = null + var _native_class = null + var _native_class_name = null + + func _init(path, subpath=null): + _path = path + if(subpath != null): + _subpaths = _utils.split_string(subpath, '/') + + # Returns an instance of the class/inner class + func instantiate(): + var to_return = null + if(is_native()): + to_return = _native_class.new() + else: + to_return = get_loaded_class().new() + return to_return + + # Can't call it get_class because that is reserved so it gets this ugly name. + # Loads up the class and then any inner classes to give back a reference to + # the desired Inner class (if there is any) + func get_loaded_class(): + var LoadedClass = load(_path) + for i in range(_subpaths.size()): + LoadedClass = LoadedClass.get(_subpaths[i]) + return LoadedClass + + func to_s(): + return str(_path, '[', get_subpath(), ']') + + func get_path(): + return _path + + func get_subpath(): + return _utils.join_array(_subpaths, '/') + + func has_subpath(): + return _subpaths.size() != 0 + + func get_extends_text(): + var extend = null + if(is_native()): + var native = get_native_class_name() + if(native.begins_with('_')): + native = native.substr(1) + extend = str("extends ", native) + else: + extend = str("extends '", get_path(), "'") + + if(has_subpath()): + extend += str('.', get_subpath().replace('/', '.')) + + return extend + + func get_method_strategy(): + return _method_strategy + + func set_method_strategy(method_strategy): + _method_strategy = method_strategy + + func is_native(): + return _native_class != null + + func set_native_class(native_class): + _native_class = native_class + var inst = native_class.new() + _native_class_name = inst.get_class() + _path = _native_class_name + if(!inst is Reference): + inst.free() + + func get_native_class_name(): + return _native_class_name + +# ------------------------------------------------------------------------------ +# Allows for interacting with a file but only creating a string. This was done +# to ease the transition from files being created for doubles to loading +# doubles from a string. This allows the files to be created for debugging +# purposes since reading a file is easier than reading a dumped out string. +# ------------------------------------------------------------------------------ +class FileOrString: + extends File + + var _do_file = false + var _contents = '' + var _path = null + + func open(path, mode): + _path = path + if(_do_file): + return .open(path, mode) + else: + return OK + + func close(): + if(_do_file): + return .close() + + func store_string(s): + if(_do_file): + .store_string(s) + _contents += s + + func get_contents(): + return _contents + + func get_path(): + return _path + + func load_it(): + if(_contents != ''): + var script = GDScript.new() + script.set_source_code(get_contents()) + script.reload() + return script + else: + return load(_path) + +# ------------------------------------------------------------------------------ +# A stroke of genius if I do say so. This allows for doubling a scene without +# having to write any files. By overloading instance we can make whatever +# we want. +# ------------------------------------------------------------------------------ +class PackedSceneDouble: + extends PackedScene + var _script = null + var _scene = null + + func set_script_obj(obj): + _script = obj + + func instance(edit_state=0): + var inst = _scene.instance(edit_state) + if(_script != null): + inst.set_script(_script) + return inst + + func load_scene(path): + _scene = load(path) + + + + +# ------------------------------------------------------------------------------ +# START Doubler +# ------------------------------------------------------------------------------ +var _utils = load('res://addons/gut/utils.gd').get_instance() + +var _ignored_methods = _utils.OneToMany.new() +var _stubber = _utils.Stubber.new() +var _lgr = _utils.get_logger() +var _method_maker = _utils.MethodMaker.new() + +var _output_dir = 'user://gut_temp_directory' +var _double_count = 0 # used in making files names unique +var _spy = null +var _gut = null +var _strategy = null +var _base_script_text = _utils.get_file_as_text('res://addons/gut/double_templates/script_template.txt') +var _make_files = false + +# These methods all call super implicitly. Stubbing them to call super causes +# super to be called twice. +var _non_super_methods = [ + "_init", + "_ready", + "_notification", + "_enter_world", + "_exit_world", + "_process", + "_physics_process", + "_exit_tree", + "_gui_input ", +] + +func _init(strategy=_utils.DOUBLE_STRATEGY.PARTIAL): + set_logger(_utils.get_logger()) + _strategy = strategy + +# ############### +# Private +# ############### +func _get_indented_line(indents, text): + var to_return = '' + for _i in range(indents): + to_return += "\t" + return str(to_return, text, "\n") + + +func _stub_to_call_super(obj_info, method_name): + if(_non_super_methods.has(method_name)): + return + var path = obj_info.get_path() + if(obj_info.scene_path != null): + path = obj_info.scene_path + var params = _utils.StubParams.new(path, method_name, obj_info.get_subpath()) + params.to_call_super() + _stubber.add_stub(params) + +func _get_base_script_text(obj_info, override_path): + var path = obj_info.get_path() + if(override_path != null): + path = override_path + + var stubber_id = -1 + if(_stubber != null): + stubber_id = _stubber.get_instance_id() + + var spy_id = -1 + if(_spy != null): + spy_id = _spy.get_instance_id() + + var gut_id = -1 + if(_gut != null): + gut_id = _gut.get_instance_id() + + var values = { + "path":path, + "subpath":obj_info.get_subpath(), + "stubber_id":stubber_id, + "spy_id":spy_id, + "extends":obj_info.get_extends_text(), + "gut_id":gut_id + } + + return _base_script_text.format(values) + +func _write_file(obj_info, dest_path, override_path=null): + var base_script = _get_base_script_text(obj_info, override_path) + var script_methods = _get_methods(obj_info) + + var f = FileOrString.new() + f._do_file = _make_files + var f_result = f.open(dest_path, f.WRITE) + + if(f_result != OK): + _lgr.error(str('Error creating file ', dest_path)) + _lgr.error(str('Could not create double for :', obj_info.to_s())) + return + + f.store_string(base_script) + + for i in range(script_methods.local_methods.size()): + if(obj_info.make_partial_double): + _stub_to_call_super(obj_info, script_methods.local_methods[i].name) + f.store_string(_get_func_text(script_methods.local_methods[i])) + + for i in range(script_methods.built_ins.size()): + _stub_to_call_super(obj_info, script_methods.built_ins[i].name) + f.store_string(_get_func_text(script_methods.built_ins[i])) + + f.close() + return f + +func _double_scene_and_script(scene_info): + var to_return = PackedSceneDouble.new() + to_return.load_scene(scene_info.get_path()) + + var inst = load(scene_info.get_path()).instance() + var script_path = null + if(inst.get_script()): + script_path = inst.get_script().get_path() + inst.free() + + if(script_path): + var oi = ObjectInfo.new(script_path) + oi.set_method_strategy(scene_info.get_method_strategy()) + oi.make_partial_double = scene_info.make_partial_double + oi.scene_path = scene_info.get_path() + to_return.set_script_obj(_double(oi, scene_info.get_path()).load_it()) + + return to_return + +func _get_methods(object_info): + var obj = object_info.instantiate() + # any method in the script or super script + var script_methods = ScriptMethods.new() + var methods = obj.get_method_list() + if(!(obj is Reference)): + obj.free() + + # first pass is for local methods only + for i in range(methods.size()): + # 65 is a magic number for methods in script, though documentation + # says 64. This picks up local overloads of base class methods too. + if(methods[i].flags == 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])): + script_methods.add_local_method(methods[i]) + + + if(object_info.get_method_strategy() == _utils.DOUBLE_STRATEGY.FULL): + # second pass is for anything not local + for i in range(methods.size()): + # 65 is a magic number for methods in script, though documentation + # says 64. This picks up local overloads of base class methods too. + if(methods[i].flags != 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])): + script_methods.add_built_in_method(methods[i]) + + return script_methods + +func _get_inst_id_ref_str(inst): + var ref_str = 'null' + if(inst): + ref_str = str('instance_from_id(', inst.get_instance_id(),')') + return ref_str + +func _get_func_text(method_hash): + return _method_maker.get_function_text(method_hash) + "\n" + +# returns the path to write the double file to +func _get_temp_path(object_info): + var file_name = null + var extension = null + if(object_info.is_native()): + file_name = object_info.get_native_class_name() + extension = 'gd' + else: + file_name = object_info.get_path().get_file().get_basename() + extension = object_info.get_path().get_extension() + + if(object_info.has_subpath()): + file_name += '__' + object_info.get_subpath().replace('/', '__') + + file_name += str('__dbl', _double_count, '__.', extension) + + var to_return = _output_dir.plus_file(file_name) + return to_return + +func _load_double(fileOrString): + return fileOrString.load_it() + +func _double(obj_info, override_path=null): + var temp_path = _get_temp_path(obj_info) + var result = _write_file(obj_info, temp_path, override_path) + _double_count += 1 + return result + +func _double_script(path, make_partial, strategy): + var oi = ObjectInfo.new(path) + oi.make_partial_double = make_partial + oi.set_method_strategy(strategy) + return _double(oi).load_it() + +func _double_inner(path, subpath, make_partial, strategy): + var oi = ObjectInfo.new(path, subpath) + oi.set_method_strategy(strategy) + oi.make_partial_double = make_partial + return _double(oi).load_it() + +func _double_scene(path, make_partial, strategy): + var oi = ObjectInfo.new(path) + oi.set_method_strategy(strategy) + oi.make_partial_double = make_partial + return _double_scene_and_script(oi) + +func _double_gdnative(native_class, make_partial, strategy): + var oi = ObjectInfo.new(null) + oi.set_native_class(native_class) + oi.set_method_strategy(strategy) + oi.make_partial_double = make_partial + return _double(oi).load_it() + +# ############### +# Public +# ############### +func get_output_dir(): + return _output_dir + +func set_output_dir(output_dir): + if(output_dir != null): + _output_dir = output_dir + if(_make_files): + var d = Directory.new() + d.make_dir_recursive(output_dir) + +func get_spy(): + return _spy + +func set_spy(spy): + _spy = spy + +func get_stubber(): + return _stubber + +func set_stubber(stubber): + _stubber = stubber + +func get_logger(): + return _lgr + +func set_logger(logger): + _lgr = logger + _method_maker.set_logger(logger) + +func get_strategy(): + return _strategy + +func set_strategy(strategy): + _strategy = strategy + +func get_gut(): + return _gut + +func set_gut(gut): + _gut = gut + +func partial_double_scene(path, strategy=_strategy): + return _double_scene(path, true, strategy) + +# double a scene +func double_scene(path, strategy=_strategy): + return _double_scene(path, false, strategy) + +# double a script/object +func double(path, strategy=_strategy): + return _double_script(path, false, strategy) + +func partial_double(path, strategy=_strategy): + return _double_script(path, true, strategy) + +func partial_double_inner(path, subpath, strategy=_strategy): + return _double_inner(path, subpath, true, strategy) + +# double an inner class in a script +func double_inner(path, subpath, strategy=_strategy): + return _double_inner(path, subpath, false, strategy) + +# must always use FULL strategy since this is a native class and you won't get +# any methods if you don't use FULL +func double_gdnative(native_class): + return _double_gdnative(native_class, false, _utils.DOUBLE_STRATEGY.FULL) + +# must always use FULL strategy since this is a native class and you won't get +# any methods if you don't use FULL +func partial_double_gdnative(native_class): + return _double_gdnative(native_class, true, _utils.DOUBLE_STRATEGY.FULL) + +func clear_output_directory(): + if(!_make_files): + return false + + var did = false + if(_output_dir.find('user://') == 0): + var d = Directory.new() + var result = d.open(_output_dir) + # BIG GOTCHA HERE. If it cannot open the dir w/ erro 31, then the + # directory becomes res:// and things go on normally and gut clears out + # out res:// which is SUPER BAD. + if(result == OK): + d.list_dir_begin(true) + var f = d.get_next() + while(f != ''): + d.remove(f) + f = d.get_next() + did = true + return did + +func delete_output_directory(): + var did = clear_output_directory() + if(did): + var d = Directory.new() + d.remove(_output_dir) + +func add_ignored_method(path, method_name): + _ignored_methods.add(path, method_name) + +func get_ignored_methods(): + return _ignored_methods + +func get_make_files(): + return _make_files + +func set_make_files(make_files): + _make_files = make_files + set_output_dir(_output_dir) diff --git a/test_proj/addons/gut/fonts/AnonymousPro-Bold.ttf b/test_proj/addons/gut/fonts/AnonymousPro-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1d4bf2b5265bfc9dcb92eeabd5d3691035361a9a GIT binary patch literal 107624 zcmcG%3w%@eeK&kAl6;qCTb3-}Wg*K#*uuz?toQ=h;9DHW^*kY5;}`?R5C{;*7{iA# zj}gZulmrqAG|SqQF~ZBkWXYvq(p;9NS&}9;ZyuVFlF`fBEM1ayDeJP-KD^)G|LE8j zA!(oI-I`kRIX;~8zx=M>-{o*T$8kD*s5tK4KXER(`|B^R=lEqG;%v8n#XW(|fAe4^ z$1iW@IQ380ZC$(lNQ&_&$A1M^i$7nt^P&8N`_8E|%j@1?VXow+!|EM@-@s{4J5=-B0zc-@0pP{XOFxU%!as;{LI3!`k(? zvQGYh<15bLdQTrt#NR8th4K6Yju-T8eP~y)Zjl9l|7Q&N=UcX|TdUjr!|!pt2Y*-n zeCyg>+f|QtU)I)ib}Aa^|&s|)f|LEbs0O1c^3bYtqIn_Q4v z83+o_tTDk*y_y|xIN)^EtH)HDn?XT0rqSG7TxvF_n0fq&{nO8jH8Y3!Q&Oke^!sm% zr#L(hOCg>T|Ax!r^0@B@^PNGnGbmgi6SX&k`MO}tR4~RlrolmsE|`D=T`+knsL=&e zrh=j_m^Ou{1k>54iJi%2f6bke2dBe$bo$8P^f=Hf2W;3FDI2y1f6?F|oTcGZj&QOj zQRToSgQ_X|jGYQ*>4Nc7!DL-9m3HPJBlFHfQ5XI5+o1*qMaEgsoZ{ zSbkEGJTMI#7*@hcD>1&gXm^pYW?4d9k*UaD$G7EJnslJdy{#wGm7MH3E z3vFJ5F{MOwmwCLHX0B+ADYT{Jis^c-5R-1<%W&lJ@OoUN(eMN3-_X5iJyo->r{d!2 ziPn7`F4f-~M^+r@t8v{o8kpd_Ivbh;Qflk670o)sm8WxGwDA|Rsyc?+_l>8YJ+G}> zzjuYA&yEO5vplt|Pq$jm)!aEf$dHj;3TCnG>_iw!;zND@L?HV}? z=jCd7cQ_+Xg_YCB<>Ip>u3F_-X3}U4+Cr^ITc`DFd$l{Y4{MKUPirq|6W5HtqP?oc zN}4nohK#}tPexsaKchEeXU4-B$1+Z5T*#nHGOlI_YeEZhwOE?q0_XVa3*KEo|0ZsM zX@Px#Z$aaN)eAN)7+Ns8;KYJw7hGD9xMr+8?&kRG)$dlb%c@P)_G({sWA*ClP1Qry zqtz#>pRK-Bowz1gq6@CX+KWyw8Y9d&5N4dhTrM2Ah)V71<(N#CIt|1SryDb?Z;roa zzGkMI$C@+DMdosIgE?UCGw(7#Vm@v@W4>s{&0V^&3iZuprkDz2g{{I{QD3pDVq?W% z#Yn|BD*m?Odli`44=a9LA*@+k%JVj!FRK<@OL7I;QcFRJ;ME&kOFZtfLQ8?>%l;-# zOn-k=Tsb{%U(x9<>#DWeYrD$aoh$6esvM5$YKNnWZ?M;PdR$#A?DiF1E>CBz{Yg4a ze^!0)*~bC3QTkWcee@6SbJ0JkD(n@$ zipGl76`D1&Br##}P0CKFaiCU&2iqeYgchhxxY<~d=H{}bm?C45t;kzcU$m+SkLxYk z2?hl!b11FU5Md59xuNH zKNgK9U2hP%a`M?Gt5(`m zvB4qzP_R08tVp+7mzU-&E3T*$+>Iw29)IFUO>=qoaP$7t6%CKKH}4HN?af>2YVY?q z(Y zP77^!4B9S*tG|PWiUD245GipnOzw*Emqz}IlfM$>uVnUx^|cz+>KJR9B~5L!TD@w~ zC7Afkb<*p+XPxw`>>p-Jzgow8q}SK+nb|)S?)|~}qesraBAw;^ube+}^!yKaKSsrI zNzlPTE`jrg1+56N0^JJL3*8zAO)AI>5sT5qLVt!6NJ~caV}dTIf$oETx1^Ze<`ft7 z{3&QQ=^#Ha!4L4m(xd!Cm-&aJNAa91(xbvKcNy{-l)}k(<#AV@Ew7cgqzS{)$4^gO zp1C48XJL7r>8D1O>vu{N%`T438KArm=LYpw0y_NI29uVmIt)3&^kRm zje8~DQi-S3es(Wob@@;;kFEJ`i7p6iMTcAJZp+%{6+1zEJPc*XjLwqFeod9&8Ttz~BtV>&6O5s1)ojVANG$ zDzF#$3K|Pm7iiW57feBQ$Rxm|i6UB(u^h=n0)}KYi6*sfER#e==1?XR8JQWGMVaN9 z4Vi&V+$C8zmcztNjxooUxCJJ1^Hvi#cyfv_6c~+vND!5-bwEyPBjvW&>_wWDh z_jm00{%`kp`UPP{yClt&`VR&J?X8lqyoJ#dC&oh}q;jjmDM=A}N{-lMY=+8oRhX_y zvB~0b5U(64yErDml*C%b#gauyc3&#!&1`nNQnaGQO*3$%xWxFGum8W=m1_r=t{CoJ zI(cP6_*H4MD{Hd2^WfUwf15bZh|L|vzUFfM!8|7!nt9Bqi^uOE_Uj>4d<=3Jb@4GyGQP8favQ@R%_8j{Af4$Lua$u!6-ua?m z+7*_LzRdq;-5#H-$(jDxq4z#3?Rt!6l?7@&j#-s)--UH(hQ4zoWs*$G+|B4EF4L50 z&-7(BX08Ukgi`0Uxn0U{5OM1=~`l)UZBtHb_jaC?2mHIMIIJclNf#{(bxt13iPspReur zm-ck++5bw-&eI#(AKzMKS-!SnMPGxJ@9`h(@s#zQ+L1r}WS@Uv{pHNE#?rP82afcg zAEPwOiP!a z;nxV-nNMhMRoL6}7?YLz8f@{P4f68(SQ?3xwB3xz;?hiM_B3BwW7=vE8CIN3{7mPV z2`*ahR46ZVZXdI-k6FslXZO*pG5o=O%wqc($}^Zj6t`~}i4QVAVj%p}%_%Z9pKm#E zzPsyNLrt--#P8d)blpnp$hP_+%cNL3^K@sh{rH0`_zT@<_BNFF9q-B6anRAU-naiq zWv%e$W$FCT@tMlB+4iqCm15h@TBZ2HZ1X z;Nf1y8Zrz;hH^uL0q#|nI+-2=CMaP{P+}~xm3T|)OIDQ-6O`;MVU~1+zGXYk05=KC zr4`aWY^h>oGKWzE(^?#_-+OlL(H9;lUjE4IkM^8jzTLHTdHY`miu2ALYCqmzJ2|p% z&rx&jrp9fD`RBHr8>%5eeBkEyAE>zN8^zWYd(N!y`^$#C=Qr*-ePVBQUu*GEq9O9; ziLQ0<4Cl46NpH5x&Pv3OEbJzjG< zc3nKR=XiU*@U~Fg^R0oI-(M8_Rb2-@y2VLXU%J2-^9jG*`xE{c>DAw1N6%qLZJZ0X z&)ebZq2GE3UZ^Q){fX;g|8=+MTtx@3ohF<7nX(*=rJWX-xiu z4A{y)hqmn4vBl|V z>h^lqHaZ-QYrWp?CI`Q5%1z-rHJ ztMnZ&w$|=BvtiSIuv+h)(k9n$b?sK{Zy=N+HkTk>) zx>sR^|BlTpnG45|z6*=8H4d8w$etTwV7gQO5h^EUu8?ic_1^la@SgG<*ybv-xOp#u z3m;3n1_e&y2Kk@zBSR8?+ePyQW+@(mj36xV{%}lEEFQ!qC6hgqygQi@U$QCLp6p9* zOkSM~o&+kHGlkEwJOz%7;`VH4y=<&7ads+EPU=u9qny-?)S}e#)P~eRs%)KURb*_a zBHGMG;@LpEA|7(LbhWszy4yP2-1pSirAz*TQyE`P%H^4a0CxH`=(o!b=Mi36d%^loghF%IeDeWxZuP%N{N} zR(87VLK#6)Wmn4}9bw0|6S~)F=q&8?bk=qHJBbB52RaoNyax+%k75Qa#lcdnPI#?a zhXG}(SAk@8M698NrT36r%v+U-ui#o;&FFoqi@hR;`@g0Z++kM5!mGWu`fz& zx?#^V;6s+1M(_k-PGn;c(<XD%tv zulkcE2J`Z+s?@@Qte9`7rx^0|aYd^Rt>18HmCd&5(1x~QPsN}Ap!4x<_bxGJEV+04 zV_)sS@2(7KU8>8JyC^3q$+W;|T9~U7&aHTKM?<>JlOJ#PEzG#9d-t-Yk&RV}rb5$K zq?G*ZqLpjPHa-8?pRPLg#z6n;$5!1{+B59x{Ig?cR;_;a*q?pXNNNXmH zjC3iiE1(Qx77gvhBj(q3%0jp9Zx_zj?%GKDi-v^P_D~?`_%8yLZoL zjuq{Wi3vyh3PA>c7rxw^A$ z#f;Ye6YEBwA9A|~FCK6Nnth3vFXz^*UlG{8A}g!r!N9&zJAb(QsRt^_X(uP%P+a3U zDWz5ocHZTG^n4%?Jm`OB6cU)IWD-=82z;!EPz)opyA6c$FhEQ*_F zhJ2GJ4_&)<=p>+rkHVYPru#!5?*I9}O?MKGs6?s~xT6dr=^f=VH6 zN%1Lo2=O!sici6LVL!7mst+WifH2r)>G)-7pJGWA!mi8JVQQak`*CmuA{}~u)YjHSL|Rza0}nhxq)+8Y zAMsa&(F31MY-1?X&F3P7T3WTW%^4sFo}7`JWPK?;~0L!B9$@B z{2~x@7D!gMiJ?u(fkGkdGQ}{1logni#hwA)1b73Wsee-HoSfuOO$rsXZ!_-+h{keH zfp4_fMVS_Xc-`TF$}<>H09GLyL~Idw0gwXhbAp0vSpae3aiVtS>uo>ZoRc1j+*TqU?JWHO_5syU)EQVyQ{^j>j4VKQ^bxE0z{Mx=l_$$p{@B>abPaVXVXQXY3SCge?wngvs;uy)2w zjF3%6lg;Ec)tgqCh>%S?O|W^PNnk>ZZ?X(Am?zefVJWhdTN*3@xSzNCgcQXjtVJ;? zh0nv@S<80>2=LBY-VgBm^N2TTO2WDW=O6V;M3M@^5|4<%dPEX~>1?0$5sgWS60}bV zH1UMp2~2Aym=f#>zJ$hv)d>t(xHH12D9vz+h9GpPPQb0CGc{0@*sCRmIh}dxIdyfE_{wds}T*RPkhuBY9T4zjpLKBA)&L65JJ$2@@8GMPjUnuKFFLr3BqCWqbOb2K_u10A`;?is)#269ek zK^Pp6+J{U^HKy89y{Yx7tKff)_onWITSjRdSUYU!V%pHfLkQ5q$zPnYxM*?t;)cZm z#BHND6qX_kLsvRE_=UC-@ryRsvF^mS_{R>UI*d7P)9cfw-Xk6M$uHXIp&R3SQ`a>L zmjvO`c*fq)yAR#A<&izV06Cbyf$qi(ehTMzN6zmxckVp&BUg_4=5Br6M{29j!ggbrBR#hj&lLHbNO-^Iwmy>|HS zqx<`B-00sg=omq~Hma-&@v^J2N4Q7k1O`)T}-TEma zZv3)vitxI{_#1*^6%mllhRwDq;?RNvi*lfZ#~B$YBt4eG$RNd-VoUL+)TgXM>|wk& zWhX)sp;&baRxN1JjTI9e6b}_MIw;O4E-EfBZYT~E19d926yu_Z0g25geaTpx#a2KI z3gF6Y5rU`*YcnE-u)k{4<87T!xF;rxn|i!!9<(g)s;vL|_PV8;4z_d*TP7xkdOHU5 zDmp5fjy%-F_jVlWDXVE7zw34}ItY{KeRFxEmPfk0ltF%}lt+GOj!U$^R7{V8r25hrk=@dj_+b%wH zDL6Uv#9N_@;xl3uZ=UX)fSn-S;IET*i`WS!Sn(jvEGCnPDyN`I$b=`EWK?uVlrqb} z*fNx{&2HEi^R2Q8NLhf1q{vs&Bc&Uk-m>+Z8%rm(@X~7AOSF^xm2Knu>gx7g*e2FZ zUo73!;qwLVFBKbL*^nQ613aaHL}j$Xct9BkQ(MXb3mHQ%gw#A~XAFR6POx}G+Vr8c z=^Bqn3wgfK^#7@f!@Xa^y?=pwCnB={`FpeVSMJToRYB;K2eE%M%Z4I*YA9CzN>IiC zt&abbR!>V4fARX&{vB6ee`nWyKkVE?V-Z|4Z$gzm&VMFp#BCVU2N=^uj46v7l*bfB z6H(4(lr&Is1jvdEsW_H|1bY|m84O8mFNi1+hNW%_q0&&Q{CA|ufqjirvS&+CqO6Fm zDT<7u(hy07&6$?QzC;)%7bSnw=xC$Vu|xW-i+{TPz(^batquPrJv71Fq-zrs{5Ad! zq_ExK&G>XnHq7Y5$c*&d#;{1(T}T%zVt?f6C4#IHl~qo|L7H-)&{!~yc}k=KM9ct2 z7$grMRAw4ev?6&bq*Eu7aEL^KvF(Vc5k~;4Q^qe|?2>L=nR#qV`e_&c&5qNjI{0s@ zx@Im&r!nddp$X$1Myz$dg_XqpDH(vtj7zkNuggz`fO68~z>#X;0;I;3Xi1cNJG(*9 z(KK{i81XFKa$-a}zQ7RE&kGGw|78TiUgIDC-bL}W^w0b|Fzop}jD{b8hPEN|FNU*( zHFL6p-5jbDkZUB+$g(PNN7+lTNiBTn(@&*?F%1)+6@&MqG94oLt~!SIil->kA=s?Z zG~=qh(m}x)c^XGKAM-eap2lEanhEF(9LB_s+blSxmQO$BL(;*^>bD8yz4eOVWW1%} za&QMiljkHob~s{Z;tm$ZrQ$FCS^D47kN=rJ@~Pkymdv~*{F=#3WIZAslx1fqnI7pZ z(*x;}80{~_tH|2f%lbFLNrC#bwae#_57ZL}g=6X8GJHF;ZR_MzV@vU??*r5SwZ)h&h))Id#k;C{8sC z8zXTRFzL1IgdrQs z%C*qw7U_caDde9hc8|e0AE8Ea9-;^a#I`+cc9qK7*x)RF#$8p=vbS^Q@Mv?pwP~v* z&)ed(#0sqy$i-s%gyM*L&WxOx0%y?T3>HMJ$1(D{f&an&=UIjG3!-16Lp+yKzX zkf6?`H3OuP!4@{dm|@HCX4Ge_%Akcs7AF=qHJP@3acPAY z)Y5VCfo01cIN8xMR53Ye?RO6xJv!j-x55|oy4{V#-`#xw_;5qH+yANEae8FrjKfY+ z5EM8J3S0mb2aq}3q*VAtATgGM3yB8GNY#y{K%PJ*102d`gqLl|F3k31*Jb;&G1v{+ z1KA4Bm;&-jQPw312T96-;>!h-pv!4pj6+67X~qm=k+Iy^UsLX%2N4hdW+8PGOEGr!L1&NlhDa29QaDtAo~v(HW&A z8IdR}Gb@nE_fYi9SU#J6zA@jH@6E5zUzJbO&)=C3vJlky^l+r5z_cb3noGL65X*9| z5b>4Mu0$*r63e2O3w&r6Biu($i5H%IAwu(10yge3NaNpQIvHW30m9OWMZCAJ~52V-Q4lo;FgI;?x`Oh z-@4=C{-$5+PWJRXrQ`Q^?^{}Sq@$)`IJ9+hcz@$PN8Ubgp=0aT4wC7FiJgLHk;&~1 z3k)$u_C9j>fp(c9PEVB0!w6k53Zinsn7CE;Rzk`dNq+XDbL&o%C;Il0058zSm6!!}E7FiH6K#IHh7dSRbP?P9MaD(8MczgA zi&ib7&0n;05$IZ*vWVy!NjwWkKp-of35W$53yKz$FKAd0SO5W$o3#KJK?E=$7jbhC zu=_Rwce_~X3Ui}m`mP!L_PXDm>p6V>{{72#&Zl&U$^q}dozjr;Ri429_x;3%@sLTWp4DwM=h zKjhVma$BNGJ)%}`)&teSjH6Xh52@KIs58_>>T-31I-tfXBykk&VgQ|-szDeXQxb-B zaC2Kr|KpRBd1v+#vo2`@4h>V6S!@>GP`3P*$xHRZQUiC`9;<)U7N;1PTttD~Pap9wPjVbW{3=E&36cPXIL>Arl*)y`D{2f31t`IIHtekERR%X?xZ_IhBWdPLfP?g_>u<@ zCAOo)SQP6kLu2;jEWMKQN`-_-CDzaTGUA1I6f)#7KsTFxMU(LyJ+s6!gOTMoAdPx5 ztDxFEnQf`co4jK1H&lKl*>ZPF_eNn~&C-JhB%Sgto*KEQwQ*Tp?V-a8uVOX+&v{kZ z{{IGErTqBlNxI~@eF3kEr+&>?HKJXgW8=30$CmTEky{8fi%A-C29Vr6zFEH;$=t|y z(wp>ly-(k$UyU4Z;#7dxuugIfxrMo&+`3#pjL-26xdX6Co$D9Woz2B zyr!?gW^3rHS-z>s#$VYQ8g5{k`n&t<>-T?m^U@uyPG{?mr8QgHii_K}kTfZU4@#8& zKVau8oBw$`Ujd2#XYBk)uBBYZgt|0~Z1F@KsV}qh#Z&F`&3wr%Y_k_f41Ka_u~uXc z&{{no@rhs$kT(R^NT#LFJ4B$IEJIddmM5z&%MVXzd_&d%93+`?qMXbqGGY=!Sty03 z$Qy#$mut+m<$81Lb5{{1_U7&cB}RKg3${JO!-4utt*E z=0dx-!{JK)F-pgPg&%Ai{_Ey_)1CeYzSp_;!ThG7j)vi$ihK4x)$+h(OIyp)fVFi+ z@&2d%U+?wt#;-nDm(^5xcA4AlH0GL2+CuH1q!qX+wCXLdL<#{usRx$V4b}NezMfkH$F61X|HTa65f8@_1hX{*wr<76b zdW4{%(~)-1vO8D>qgOnyd-ch-N4ni_zdgy1Al0!U;E(0)NB@_nd!&^=dYN#z%hJ}4 zKyL%hgdyp`rslLAOj730Gfqzjr^}cU8K)cteB|jdhE--1g(}aIHtBb!3PI;&nPM(# zquQqSs_WIOV3Lmas&~Rt4JGMhm>ZadT!e}F$PQ?syA+1$gGVBUDXI!y+yc{-e`dP# zgI$kYei7rBZI@HG;p)o3*y<_=3fvuys{_qGC7rn`oq6wfH{}%1nWX|M%5R~ZPJ9U~ z6OF1XH)E!kau79t^yn?FXZEn+Moz&V0yk|K3HR^f`VIHG!9iA5mE-T7g>^am&J%WYiE)q>vf} zAV2^R60wa1I)pbux$%Uc&RaABwbNO_3m%J%J1iGNJW^mCyW#Mr5cGli8~~^(sMg9A z@nvwiMC-|_jg9uMHEX);jT@^@o^;##8?Ci2v(0@{I%se1t>(|Rl}9KmSV~8c3{l>z^yulwToP`5(T7MMZmgh_?Utd7`NsS=C%)*{0D4qwc!FS zq5S|ia+8sUqGHe{!o|>Z0GKgXm>3F*!h)|fJ-r6moGfxn(=(DAPqU&rVwxo+A*EQ- zw9?ruk$h*_hY6WowbrHwF`<)Hy-oJ6&dx4-Q*YJD=NxYSd~r*KMWY?);?H_)6F6Nh z`P-cnrEO9hrVF%+zctPMPCO$7!EwnPb&E)0K>#CVdjkkVjV>)S6$znWmIfefb(iX1 z(KuRm+}+ku>9EWlb>3CXhaWtk?Kru;rlq@O$ARR)H+L+*_r7~?-IDWUHIE8~!1g)B zNFK$b`%zcI@@F*%Q9w+UQWx%=m%J+m+8;!&D@#! zaOSbh)0r1CDWy8|DhlXAMvi4b!|2ba68`*m0Tv=nJKvOV&-djw=C97*ls}X|ntvky z+5AiSiEBa`@dPA>O!G7W1G&hQneWeq)oKg;$P5iquK~g-up5;LT3AwVVPP6XpDgd0 z|Mp2ib(_=KR%LNH_=DHvWZ#1h*HWbVcDijgH~py3(gl{PHm7fG?(emf0<8KZckT3E zA(MYbzQj#3QaZ%4U|rlJs23!ER>k7fc@+PLeF5|eW~#&BD0Fxnbq+rgJjORT29Wmw zH#F#uz^}}3Q51Mo)R>8T5ad{V46^)>M5U;S%!@RvOjDXO0jzKg$>6dr@jrYGG4$)8>Z&J40 zz;kcjIU<4eVJJIW_qutzar;?$H{`Rl8}haCZpde8H{`QyHzHUd?ef*R`+n{$yDxVb z){0S0_m!_@_vKFFEV4;w&&v0e^POpz?`8LucbUfU9GgY!+|$_H=AWIL#dGXw^0jlb zc#b_yJ}c9dd|!H+e3m^el2<}=^Upo)b7$qb$vG%AH~Ct5ZjsEC*|T^WG~d&_jAAB$ zE9BCA8#)@qxQ#Yfj8}~RDDj;3qmQ)b5OlPK{vT)ZI zSL-uzKd)N9SHDyLu>P3-wElvg#J&D1RqhkGLvlX%T?#Oo;^vh*P|$HS_eAcqxtDSi z*KlEz8g)ss6klA*YzJ1p0F?@D7oCZw6)HB2v^0;duc%l*+T1d-q2iFWy4~q)ud-UJ z=)2mg>S`X{P*Jgg{=B}ja{Wm2{J-LA!mU6R!@xK*fj11_7K0_zMCgIRa)A@gQCa$*mal0>m zCYVrRf4pTy?3ti>`o)L7mMyq*MkAifD~KO0;`-4=0J&3$bs(S<%HUao7@(Df5mz!Q z8KVG*X#oZtPSsF@9H6uC)?jxgkswMMVs#6#Nf}8+N##imNdc-`C}1Ju;DV{3mAR-6 z0Pra;C50@81=WEbSkj@MDSH%CRas!EvY5MN!qd5@cF$Sk<=l0?hRc^-_H!$`o4h$^ zYozJZlC8?(%uAdQKKNDh{!aJ)q2g`L9lx5GsOd4L*z3zXzJ74RWGgj(M3k&Y4#Y`V zG8J4C_h-Sooxx^j@NPDG#Ey~Q)2s_FxGrNGuyM!Sggv--yGgLdxlL}n+vjd{uXbZT zvsO+e+xALBWnrbKvaZr!Ny5Bxpc14Gx`3gKV3k`JTrtIkmuT#u=LD+kCwpXm^BK8n znLY>odY5i}ysh=gJ#C3E>h^7G8eDt$%;B|n zIh}Xa?0B-x|MdRu^vgMi53~)@`R=#swvN>1;-{J2%*jeld9OyLn z2U=Td+m}@Z4s7ZkzN=`Uz4NhNw<9px?d%)s4zzSsdjkhYs@f__^D=APE82Yy(_Q2z zaPR`*%cS8ymf533f}yqM8(cEOPJrczL&1773kuHpNBAq450Xl*rcOfseI~aHu>iCr ziNpfbNg7Zaur1i;U_Kpb(NsffVX7y!F4a$It{YMZD9sfa+~7&2rl zy@K>J^b`>E#^(WU+|NJxM&m#%e@JEVt{~Y<5}D$(KJHw&Ac?F}R}%UHd}-w+Cg(D^ z3SAyooy+f{$+-qxn4F7zFQgmFg2WD1v@0`H_>#>`nI>h(d=buN|FUckSd0u3k2t$T zcvKM>=|_OPnE4`^C|5k?zHj*Mb%$#P+<_%`*H!p8`74&U);L$JubVW1PMZh6;#l6g zyduyWsN@r??!Tv`-2F_cWvRnn>2|N^SlSe*Szc;gcKq;kNNjFCd$_B(xv#pdXL(sc zmc!|5-NyO{@&l?a_-~|(yy!voop5dfRk<(H7UQ!_TTZK*01g{}UH>ix7fkbm3!C&q z`ceG}{j>T@P)YDk=0z9?)O|SjSnlcE3(!8}ujF1OjJGIK2VcIsoJoOlQ@Op|SKe5@ zx*SqqkuF#X#WLN z_O32_v3P3R8z(wjkG{FD|3}9I?Zjvw) z#r89$jPE9}t|oYR#oTi4rEqz|0!%}%MU*w1nNY$yNX^sjM2@Tz&-m*l?^3C_DXzp+ zVlVNPG?uI`q4M$3k`pD*mRu@f%Uo_KFD&~SaIM$WnDsq*(8e9QapKF)v5!Z3o z8P`QuBGO0rdASx4t}JH@IJ;L4z|Uo2d@jn<-Th|kNSC{`>wy2w?nA}>?OmPz{$l>% zAKVH-f7HG9KFlGpyx!yIKCQAj;AtVO+2kS7Y}_Lif6#&Jd8#02%^BhS(Hl8BN-a`C=_zVCsIL> zD3L-mIKL(akO=;Bn^oNp>~9&~_Vm!uU(f6a?g;W%(DrFm3h^z{Akq*o@(nwr8vgkm zkYLXvJMdK{J1~;*$g6F<@T%0hntwv|+Rag1jmZ(Nc<8D01JdG`d@|Gn}Yl+1?sLr4pIuhOp;RuA`&e z6lJnr6vKI41Q5oc<}e1ie{Q~|Lkb8&$GMK*Nx=^O?2JUe^218ENaR2YEQ^_P@RC_y zr6gt%;htDbm_jKLQ-}#xMdU^SoC=VRyb<(Vg8~|hfl7gG2*U`8!W2A4KsCjJg5ue7 z$aJ)<;SZj@`zpUh7?dvXCh2{?X{K+VTw5#`M^2!?SOrqK^$zsN+brK_0VsdO<|qnqO?y(F_iJm3wKkEeA4_Hc}Th9sbKM)8fnc2&N0yfGW`mM zSRby~f{cQqg7Siff&eOdLviW?OdM7s#Z;h)tVXNN>b2HeSCQk}YuyRQH)Ke)lIfe9 z$|CH=>}xJ1)0-X>v4{$7F>ZiuWdF`@uvIwQVe7y|<$ajI^p>nh;+cc?)6@-Ev zvwgNSs<}p6eH{n>sp@!$dTp(3|JpxQwS>By9mRrBhr%Pd;g-F{E(uGUvG$*G*ToCM zD_kBr*txjlRNUjnGG7mtktssauvo^cp$zslW@Y}+JTISS`mptw^|bYZl_a0_DtY|a z9w+%j&UabCf1K0gv^#yyM(1khCg+fI)Oo`Btn(5I{<-iXHL00&bpfL-Vs3;+U<*JC z6*07O$*i&|oNaL^=Nih1nCdw>$P$nU^b#*vmIho618(=WmQw$6t97a0)w0d)9%!JW znK#|t#Wht+dVbR9jDi19GRw_40*DO!RBp#{`&|TQSgj$; zlcgO)lv0BY@UPot6V6CO)MX=||8U0eeZSIbBY$Edz+{(f%Vu!ws6l3 zL>K^}Az)ceBnifJ5<##!-2B#d>HSyEyz{dg@0@*E`k;;9Bz@y8>8RjTT|lI6=BnU8 zwC;jR?l;J+Sr(N)1&wu*%E?f|4myM&kCaTpk3t4%^7Sz-!t_)jp`tcUvMykG>^f7fm*gJEq7i}+%`#a_ zV3gBwi_-(LU7i$ibzhw%GEw2Znj+<)*q-?Q=Cd)iHX zkNC#jqu>AZJHwMlT3+I})mIx&4R`ju2+BoBh1EbObG6|ZEyb3RR|@h%gc-mGeou)Q zYj@U#&<7Mp#`NKPu)HsxN7@%G4loX@(zr+^E@H!FX&av>{fvM7@&tc{fBUjjFGS1{iqw36=xuL`Y7-P`^U?-NzO6G==kH*TqBBqdd zDmZz1G6-v|>-W#dKdgR*gT{RWK5j1eaHOXpauO0T3ZV3I@&Tw^9z|4gsS@jCu9&i~ zQ6A@)Bov1(lFsl8CY807l?(Bd-7*04O{OBMU4anLMNlQz?k%QGKYJX%+IFkpV28 zs&pRR`me9$)uQ*;kq170Ew{#^zNYEy{p{@*o>QqVYC1Qli=TfXM#XgNt3VhXtfr(4 z-RPiLJy5U806YjfUZ=vKVD?lfM~)XIC{-mTTEg8*3?+pno|3u}KlO&&P%;3gguIGU z%s@v?x0ggL7$s*jT&5|-R4r=i6r@&1%1Yv=s16)C>RRgfti7gXeug$fTcj=5HfRId zKJ6~;BiiHIGun%2W*y4Ozhe=}5@2?BE+Z+l&Q_6tRS}Cd$F3YWa!;N$FF&iRx#2|3 z(yCBDQbc?c&V!pq4)M$FfreuD3hVnmK{I0%J`fYp&LLB!QdN{LHd}|`ELC*Y%zV1F z?Cz07V?l|bCRW&L@kz!Btm=o{tHQLp7`}2IV%txk8!DAcz+wR^B~KI_s@Fn@ePJG6 zCR^NjF+5t_%mKvhWSVD=2Xs}~qM>!nAq$1l@Qk#4#%ok7VTzqsE751%WqibV+<3-# z(a7Ai&n4Y4dSXHhxw&Rx+PCp>zyC;|&)0Xv?|*!wFF4q}cGu2zJp+Hmmg5=!kzSv- z_py$^(TzUe#-o7;|Kcwm?BBb0<~Xc@NWC2O`xp%ylgOjxl1Z9j-pDBj*%%GRLZio6 zXY`|tV|;^g03#;kFFQHH)QUICSa(*Mkebc`@{&WMB~nZZ=k$vkV3B{4Hx8zL;t9Uj)ZRDXt~=b{(9nOl&OOlAZjw$;6#MzKCwE+Zrl;o_gg&V6{Rd(Q zaVa-fi4M%u!6ty8&S2&BF`f2ih^u6#NfYySP(Z^pSSVLMTd51$VB+e6Rip#PYT4S= z8fpt`J+*bU{#shQ+JRbyDxxHz8S7+@((;OMF2!F;aS%?@P&srdoDk+9EHo~(E%Yv| zU$}}Axq27wgi{jYN*6{%qMlq006CzogjjuszDQrLZ_uM(WI)eTroEo7gV*MnA|V1V z*vG1?mrVtiIfDyz!8&Zh?WH&@{2ODFlh$NoIo-^tP$}DB-i9V-e$Jkof7!ONwW@Pj zzANzHGDDuRytu$`+3P)7)v(=USzp(6zgH{mtt&Pc$DgUO!{%_RCX%v^ zrOkG;w{rjT)g^at@V25#Oi))gEwxq*`tSBNtf*T5t=Bj0?Xo)V+OjFxQo;XXu&rZT zNDx+5yPQ2W*v${Hn-kd0EJRWlbN@Q$3o=VAh~&7`>s0wPzBzk0DxF}WWSg?>*}m+? z?A0iM3MHjx(?Y>w{NfUdP?pMui$b}oRQj^BuX%=xvaL}yQ$}p7QQ2cg3OESm8p)&y z8kwsI8?L}uU@Pzz)EBIxu1~!MJK;Kp?D9qExW9-tdC?FGERc}6C}UC4qVh!zivp;- zSX@dDpb|MC&v4E)gb9RB%M>)Q>fs+!ZIvy@j546DJ*StA+H&$&4u`g_$FIVNX;PCV z;9kC|!8Y@wG=99Ux2CVjHg0zBYuJq3-75EkubaL58YrK)(!GQKm!;*>(aapA@YdA0 z`1^gx>7{tW73=|ZDWGn81L0(bRp%sSgP3#R`@>4eA$EeCNj4-GCVP_WlKtqZH@+cx z0Il}qbz}|n6l&3UF5oznI|dwQ7R3=N(GM|I$<9FsN44Dh$4X8J3WYAuz-CB7D|7l6GBVbM z_qrJ5byCc_aIDf65vAbXG-Pm`l0NzDpPm~Zy22wEfpoR2rIJIWDqUvTz>+s}d%}9! z8!`n%28=0KPSm`jY;pEHWs*wXY&chwf!h#v1-lGniW+3Hjc{K$bb1*55pbR|JhG>P z*_5G!C5f(yg6yAfFY%jiD~^($oVA)z3o=(Bb$KR6v7F$iyvl77-xKdb*KO+N7+$2w zf_k&Xk{V8qg_1+65{fkNd^t;#We3TUjf#uX?s!&wuj1T5)mQIscGd*!C+PQ%=DFYR za9;TNp3!2jv!y<;^HH=ZY-^C*%89{)PVeHj28>kczb7K{Y?gPEk&r_53JFoWrp2Yy zXTWL}D_EPo>6`qtM8*9V#a7USmR})Oh*v>J36u$iLTDgeKqF&lovFWO463m4$-g!z zJ;$#Yd_zFHP5iR+b^f_sd_CVZbY(K~jk|5e-Hy-Q4f$fwdn$A#r@PtUOy3@-cFYk^T*X zC!bjW`35vw^yJs&`>D=zL;e6#P~@3Mbw`ToOK`_EiC`GeMPC9D3MzsK$^OWSfpW+i zVnxrfSs7VHS>;&`SplT_hZ17Rb7DTH#*&|+Lkk}xTqCMagqY#y8C;|#AUkaCzmTr; z3zl_e@zAGveYh=vyDV=ZNr(aH^$#pA5&M%mYQqq*bu)LZKXjNoUR4R~C zrpky)$$|?P{Nb9_P+~k&>3a4Bhi)v7G;ZEd9@Dsa8F@u{<#`QxfjpoY2-zcr!9L&n zguiEtEU*#oUapW%VJl=H#^@{sDLU5@!B^(C7sp@7{#xgFspAY#ZPTe@M{xZPZ}Ozo zTW(cNc64@j@Wb7ET@~&K9d$%$^wz-PO_i-q{UqWw^1;1}ad3u@uNz)pc`mDTg+n_%|9~`WWG#}QH+rC> z;K~H~>&hx*v-mpNmM3E^kesYwl#Fo|ktfP#XA2$aPk8*fx*D!%oY0eu|E8Y+W-GpIlQ6bTyJ~w^xUtY1b#p`1Dt^3 zGXZ%I=XOu#ls|IMW;f&Z^}l_=g%6}hF~YtIEb&{>t4frKuFuFiHX)na66TX57Df!{ zCkB2td_-l<et~GQEjqnr|m)T(~+toi%*|)|7DT<6FNGFNhb}J#*xhx?@?w;?lSn@q&~iW%0k{ z&C(6&C%lz^Tgu^o!T%EHEYf?@dzAOca^_h+8_Lk3)g$?A(190OPUc-q&)_Wbhj~2x z9dUxaGYs9Z*_*d8YP6`1yvHi?Mk>bXkfPK(*2f)|M;T?GEresSko?8$m>kSMErwo^ z0hI)l3ZDpvpno59Q!)v%L8F{(dg6ldLw>`l=sV7*fwlMVVS$E%xgKPTh_h5y=`CjGW`Wp zptCigzwnwMymmn!XzlRlT+lD;-q+T?zk8YP+;KrTE*=b=>|a{7?Wu0-qa!2h&g*UI z23z|1^#?{qt<+a`*^Uz(=hJP5bekTO_bR_a7-qK1yftOEk1#w)F_FPHW=$UA91E_0 zPq`kJj!Lm{&?y||O|A6(!CA8g6odWakPqMl#bk5eApwCXA>M01{mQU93_W6CY&??| z&}1-v(Yy?5B0C~Jna!-o3l^+U4x(ZjQG!)LO=YYWO)Fqt3(UM$GaCt^O*|UFBsLpn zH~KE5;|e48$BNFONYjyfcim=i3pb>1VamUMCKwEU_z!t23M$Zrpj?jvZ}e;b^^D>6 zMg()SPv&yCZ`0KAIvZH@7z*wKiwNLvB486N2DAr!Y=~-^UZY%ys9*=n-c~e&Vg#`X z0xd`R9Z7zGt4M8vTk*y3xuF#X7@UPGnTJ7dMJ$yCfb3*o4U}p;X7aE(uRXIoC=F+n z8ED?0e8P9jtrxUOzdt>p+NKhy{{pH%Kvz#Ar;BRc)3}FdKe6m;5DUUP*MkUusji1w zRRCFjLmxW&*iIi)^by1d(;=|p__&+6e2j+%!M}xNi+_u~!|&KQd7{)!mRXZHgu27; z>DM6^Y(|S0IjJd;ywr3wuTyeUm;J_cD`F^6*@t) z;0fT??EO=U4c(1edZ7f7tM9-{PvVx(m)95)Lzgj>!hn?MGcCZ zFU^ILS#dsJWr33L#8d2vS=^t8lauLvdGj=d@?@rG$r>;L^?lcSrUEe+?re|2L zI0gu`W!!Hst8>-%%6Ko65Xjxfocx*HoSArC4ct3#1eIl=pJ$`1Ytb zLDo_C{qW+rR(y8Ed5}T$-}6?;O8KpjmCmsR6gU5aZT-hz_q^+YE-=M;Odh+(=V|n; z_H6PDc}6`aJkNSAc@T?#z4hHzQWtTprdE5aueGstb*l!)n_7ojM_W&{KHGW;$H5L= za051mrN6Lsk7D<`p`PxhO(4yG9}ey#UA0_|#55$Cj@i`Itbbky@{P7Z+lcKOw!gJ~ z&xSGl(Dq|AEeX1iY{4MQ<+|XXg1P_Tiy|5BBL^Fhqeulw%n(=4*1O2*m)6^kg$pyU z91|*Meh3`W?QSkLD&NO)P`H|FH01DAIYvWXp23*IpT@boJe-?(#jxaV4=y(t@XtJV z;}s64mEV-Vs6PAbMvZ!b^vUgSnDmyGdc8}QO#elBX=ynfM7}F;d*fu(J1y}BO0qa) zpZC|ZzAZA{=hcnSdAXh>a{hBZa&(;u)i}x5GaF^L{$#E$CE`=dH3h6*Wg)X^l>4x% z0u4P^KCg_2)fF%sNItLJpLH*pcOS)PXB~gpdD%CQ+&@zHLid-?v-?9YL!zkqzl~pLPS$m>ZRX$I%md`7zdi(Y4KG0TdRUu#R)RW|M zx|UuZ7Kzosf{3LaMvLe7Bw@Y4(fuXT_=V>Vi&v>~1ZY#D!t`neG}&Tcyc%0S`CTi& zLRHoK=Hz<58lx#yX~oTI3uIzDvvXmN(&i{JGIlWE^NXb*S?sk~5>BzP8L>sN<*^O1 zf!My-U9peE9*;c}dodO-`aou~UJYRE&On~H**Vf#T31!Mplqdi$T_m6bY*pg)m3L! zz1O|f>aZ>?uj^vpEMr@x7d}wE%rs~+mhdac1+0k(ElA~aZnX2W~nt;Qy^?m z{&CK54CP?b0O0g{A!58@)*~;zG&0n+V@H?j@W{wuR~OU2svMz};l^|Co@5Rg3_twq zh$bdTSl~WER@S)s4Hlc+MXw4(qD4M4<)OIEurTFL*MD?P35pHJU(bA(-pFT~|3*G~ ztK(?qiOgp+FQMj;3mYRfC2$}emW7aXAKKl)L9u=p$07EAS4 z7Auv2g&q&OPl054?i0NG6s^U90os6cBbYARg;5O3>_V#D)5Ks~)w9mAOK6b`uY&eS zb(I|}^%Mf+1E4}eXpWK` z5c$`VB?6kmVP&jw% zC{|rB?;u00Y13c>V$(#t`sv(@FKpT>eagqe!+x3DE8b9@N6>UYnPoIs>+Wz5Qjj@gQ)}KAXc~8ywm>aQ5uL;I5rEd+W|! zgM(+<4_EFS`t9v)Zp5<{kF~cSt{5B|Y;DCy#o_i-LqkJ@ZFCE1^Rn&K^Je?9m6Xi% z)O5#P|B`O6B8gxU-p9Cd5c#+-oEt}J?C>O6V$JyLDeux7LQQe=-VnMeWhiAd3XG#+?M8+Et&}_KwCGIkL%wF2|bbXA0e`!bH z4gEJ_tMxe!XKS%B@5!pWIs?5+o5Z8Fn_khjXBi9gddl(cxjs*6psvY+N@i@|2mI6E zC>>W6PA2(5E@)E3-&Hz{gmo$sOFHtAv2c{ifB=*7>RA41>3oN-wJyt7YJR#y_+a0! z(~3q?&Ba*~$}Cm$@F6|;r+^y?r^QhQd|omI+bG+NJnk_AoJu&}aDCI6>x(3${Hc&R zBM<8mDqvsq_XQAG3}j;b9)&_MiXfvrUuDGo^+=;($?}>hw3-$vxB%Wl*v?2*TbKqg zs?SZuf>bMX34w2IbH_vwEb0FF^W!f(^~0-&Iy#Ek=Tf#kQBhll(6 z5I0b;F%yu663!nkjx%FSP=F4sFklyID-IjN0e8Zo;3rBNN;o|h0Cu=UMJ45A*{E(t zsGMGFgS9EhqSqgN2_dK5VTNMRn)~RRMBLi!wEVo2*Q`wymU#BY`P=4s)KQ;v00ghg z(x>SizW4R^YSF>oE*~}Hc{^-IrJ)lN@_CsP!i$n2uKtKJoLQE+oga|e9N^KcKPWDBArHWR?k?hq8y>vd0Z9BFLe=Nz$e44 z7tp@2_54GO7DXnEkiLlZ_@fHB@NI0Bzlay7EpwD3{wG^!Fy}3J4D5YUI#AG`&tX*v zDZoz!Kg6=0!N36!I7=aQbjLT55fFjEUr=dyq1bI~eHfP$-|!k#Wf&B3&eWS|K$d&JlWpUpjDFe&3Y*?y1w_0Ny)w@glu>>UV;L-a19^ zpAstCe*SOljZpOdsTMAPCRFR>XfL!SENZYe8R2mPz)r9y0<>a4`h?dGp?l#cgM6ZN zbCB2aiNc4{C(^$U^2zLj*A7bm&L_ho`H*4M0(UvH5U^%+FsvV8zj)m;zq8I;d8hPRE3 zZ#M5n|6!mTW|P@&_L&>atI?KN2653Hg5`Zd;#j~s$otZb6|&%Np`oy_&{J4f=tmQY z@ePFo=quN-K1%IV9L&|KC11V1vH#VlTVASFjT zZdj82_|Su8yPr7U$MTpjvfSoC%@fi~vkA?!5eurSmm?O~ciMGA=dkPl1HJeZVI2hm zvqWG8!DD_QsDhBv|FNFi{|CMJZtuB`W_(+fuG_N+j0Y=Nz=I8kKaQXiO%}qI|L0n0Y!y(?SUx zz5DNG%)JKE7FafY1MOkR2eQ}aC@+L0S4g-jWYXNG z$^SxTKnYjL3*id(##M6?Dk@1y>!v~rOX&TiNWb_auuP`w5X1t9(!VaF)UpagMPY@f z0_C<9#HSSl74YA{&ed28dZ9Vif+;%%4`KP3M0WI-Vraf%DhbkeYsSiD*XLxm{?B{4H;E-^DeEj`Pje^Cby!Tlwfmq;Q= zVJn>;S^c?OX`B{LMY`+Xsq;SEQ0$DUMS5qp^S<0;zy40`_i1~cU>o+#ycYbRT9T_5 zH8(+>3mA=)b{LGDA6DGH_H8c_{8#lWe)+O%Es(Ps-yp8*Z|NEZs9LpS61ivEN z7JzydC(OJ?dTftHGI{^cv-XpI zWOjD;Uh7%UdhQp^HJG<%YSosdcKgyTRa4jG4XE9NW9zc&T3a!U)pBk=eC-C6Oor4GI2GjNr&H(!gPd@1eBA zJ%e~49NKHQ@aCvb2~!z&$Y5lVOrBNgDTObb6fZcXPyq^Iwdv+`Te>5?EPWwy?Z)&? z;Mz{3A60;Y?EH15f}+ByR1iDjE|Bk=ysDHTa}}te{^Z&7qVq&Nv>tV8OgwRQE&l$& z6RvG(?h2-cx35^epuO|tu3fHVS;o|DfhYbVEc24T(D^ve{6$m%V*aAnk)ksu?w0(8 z?`HlY?#6ZZvq!A{o@ehopFMFmu0!g~1;wUbMrP%E$lD`LH0nAz67e9Y>!6%y{6wMW zmJx>J6%yygkYpY$;^5&PBsrbJN}QDPDaOlJ$5C)WQ)^5PcL3rtObL znF6DNJi91d`FFj$f_-B#V3Q-7DiF7}7RMPpdn$071^W~efy9^!7DdqA#5pIiOezRK_MUV}alvcjc82fw6SDPe&GeL@E_JNx&QF;s(y zG(`$dg!w5AKQvKaLXfS|>eaEy7It0|C#+xyRw-zBg}-@4zcN{qluW7;eW8?lMg|Nt zCj=!MZys+O?-*Y;ej&ON_BW2-gl2`#i0JV|q+kuopeUb=_6$;N{4-)RGBOG?$}?&+ z5CfW$n1P!>y-*BP)ZSpP#WyxwP(x1~s#Z5#XV(RU79#%c%M;hpe8CHoP|y)pP#rgP zIz|{)F>)QJ6{dS5EZdNX&B}htB}EHF#9+W~i0}$(c!@~!0J0#T^2Z0*=@B~uFF$r}C$G$-cD{z*lV{GH8G0M#77!V>4QNUISK|<6 zBjOPC>fYc#cq97gQBR6M4r9O?@nVdqPZjOaqQc9)Hfod&FWF6(Lj9;V1JqoU1HMM1 z3W}|P%@HYvk$9Iv>B}T^P=QY>>PbwIc_0=~5%nX@D^8pk?CncNm7aPC5j^M)+q|r zKqQZz``Ne-E_V?8po_WOc5pdL`@CK*2cEz=R)Wp3d+6&mF3I4Cr%?|M(Ho-|HYwmM za1{IM$>unmi94_9Y_5y6E?0=?)C`qN*S4s61X^WC4(DD`G<*OLi9Q{D5!cV>C?1+v zcQjBA;#v&S*x_T*DVtaEP1)*$5zCAFxpURMSBf?AV%>@*&I!q^FpV(_BR`bEy-N6? zX^|IE3Pq@u!XwS=^f4j>K*tM`XT(l3HvrZ6Jva?@JPgB$TBV;T)Xt_%B%CPWXc{B) zmAwQKjw+WFL9PPT6R4aFBGi%D*ozo(GW$WKwOr_pV6H*oy*_XVqbL-m+M_U^jSS|r zG_v`rHKn)DsL72OeD#gf9cqW{wY|O4vE;64#)DrreBt`ENwwIN6&8W_eEd z`M=to<=497pH4OSExLANKgMBp%y0FphxzJkd6lF~oW{e5SqM@puhHRKGEgR2@`IN~ zB}w*3YwvGpF zRk~YVvmGfdt)YbFSyN+@(3m>j&0R~896KN!TQIxGf~}tX6TcN&lbDZOSto16I}g>LySaIaivo@=CG@{ zPItR8fbMqhHC;6fV2{MTkTKm)CV56YS(ljgohFG{+|3d*AV;{^qhAGHxbCHW#v4gu zCh6x^cdsf^r$9uJn4tI8g@`Id8!rjKOSEx2F8s;x?hFngR_+YvFEB+h!J>ec0*6Xs z3<8J^Ew8@ubVq0D#@hNQ*9Os}y_3k)!OErOhBw{K+Vwd?pGs5?>hUY+d`XfSe263S zgn=(Sl-pp!QicrhL}DqBl)bDaUM-BIwS;ez3*ZmTgGjZ~d=b7v(a)%|(cc`oatYdQufZa}iNpB$bHRG*3C=i!Ybc#IE*HyMv_=9!G(F|u{>47yw5F-7R2dkU_K zdq?p2Ddsog$r6vnlW88}Zh^;fuu*K~d7u&F$UHm?Ofp6Q_i~}7zL{Qrn%zxJ^jWb4HlBm`S$jl!MUK(e7Y$b;LR{9EFZ@N38?sIzHRM z=t}yUXQk@Dr|o#of{oKf098lTb@m>g)3vNPI8KkYii=(M_jDm2{wsEd`qfNSe$i*= z>JnmSln~VUhPG^vv8US$?4|Y^RE+IkX>Ua#S<bA&7+*NPe0(hw zY)Gb8LEfY6r{>Y^{i^mh_S%+70_LsnR9{z0Up@eb#Xv-(l`VceB)jui#m9 zKWk3kLKYgGMee=X7ZFO;kVQfgG)Pv@DSQR{4Q3t9LNgl#aR&K3$zvzQITX7jV=gp& zj#WicAbFfgnn6Sg^kJMc6jUO`o|SL%tP5!y5;yTL9`m#hxM_WFFSV_Iy0=#=c6Fh; z*_M+Ky3o4bm5R(RV)&q-qJvPfvbQhJqk|B%6-X9wOhqupAQFYhhF2(!7yt6I2D-gS z!G2`Vfr*c57=a@K0MMx*otasUhY|r<=-TbIfRVb9pH0rtkRXX6lNYs9P$im~wnv{K zz0=Uei(b8kvQ7?&WW4q3*kJoh7JkEJ0b7-O!3CG%$%YVeB(vs;SJJc0JaDo3ql5N3GAU#N#yl% zSV*2h8BCx*HSq$9E{a$zLVp~y{<6bZm)U5Drl1FbOuap<}NsatflH@6>33tace zs@%cQtm+xlt8WY)+;UI49%^bY?Rsj1q4dh>+~&naMYU^k4dqu(_kWhfhHjF}V7-Ta zm8d*}n&y;mGlp#VPJ%3EIuB=Mfj2k7>qIXCXbl&y$*nv*~!Zw z;LgmU6U3Ap0vwbL5=2Rz29To6QMM>YR9VzQcuM;lQ8nKUqyiK!r9*&9Umlh0JZ%5- z*w`c*RE^mFxiOVCWu#LTg9eh@IZ&>58HWlP_kSZWPV|{#9I9p9$91w$|dJ%bdKGt{U2z#D)xuGY0$xik>L&szZl045`2bS-?&+Bfk z3B-3lHgY#4$R3g)0VR?*b-5SPOAkpexGMPO2#35bzDe2G#(Hfvs_DyoI~DGIcD$~> z`LJ&f`%m#R+_PZo!HSLT?0I${4G9G^^S+^w0Q-rozHJ>U9*GNBj@{awz6T)kJ3Z z1&Xe=@ZTCas+v+1acW1k9>@;2Fd`G97Z!3bdf2B!u?|+rr4@|ULdhlH(vn_#ZRq`* z59Qugs~%#=xW(8Hq2vH>c}e z?_3nefNwlzKHmBh9+C+k=PV;TqKTzR<$?|s1pRQuU;5FCWhno zDVTDy7AZn2ClnYCfA)@RsoK5ZP-oT3#?Fq*Su?7$1_r=4v$DH8E?(^DRvVrDK9;!3 zZL3!tNwwXp{-UVd7VLUB*jDZeTwd?$x{c%oK!GCRe3To2XOKe0lo`kp;!;edC`bsX z2LU!dba*|4*y^h&hoJ2=#PuHnFed3G&f_=@pkH5^rE`^r>6}^U4Kwt zhdTph)b;%kYhS8YKh-^Bke^}f^vOA8gh+zJtB$ZXFG4k{ZIRE4+{V5DQGW}LEt(gE zYnm3h?Z|0I&N>vweOt4_bIev5t_EO6)Jv}(^>PCo#;|XqZV-sxTcozcvX?kECN@2` zAhtBN1`TlgSH`xYFD@W?DurTEQ#Nyr@PHIcN@hw?N=3>d)PECTWP7(nHfdu(%rO-s z1<@MI($ZM-Sld{~*s`$;$C9)(cGFlq98m^nH`w27Yexz#R9joDEyGr5E4S6!NVO$A zy*mHZyMK`Op8C2g({;IU({tOmKX+FF)erA}uFXCeH+5re?Z&BbaZ_7RL0tRmnFZ9| zY~#S^Td8RH#%KS1Yi(~&O-)a4ZSBc>Q8ir3ALuQm<1M6au{^Kskys-62;nQ}um>yg zW+Kc$UeWG&?KPcw=r{te1Kx98o#7>9<*U7rL0ZH8+5U}pTRl3%ANtI7yargYPYrYx zX-gYjzK9hC`y6@~HhDVluO&u? z4;lTx{DKO8d7Ob@^vVP;2A|W#jCwsS}bjkIu9wKW6}QjUDR{t zFM<4p)*g=hGCC5Gh#+Vg-1_*I!M`3q{)M5%m8HIRWhah>lal;KC{zII=ztSb!slB= zr1)5{0|2KV-ZwEjFZSg%Rj)0PoB^}rV)%Ztae#HiNd2j+Q`n{rPPa*#WG$VF`$3+G zRPNPn=P;aezR@WabPKsNsN-S8cY^H$<6vR{^M{O#7y^(J=>90wAOqRGJClb+(KODXLB*2V@^02v$)}w^{Cuk^t4Q4&c-mqE#J&n4_ zLqW5cEjEk8Qf66*HuC+AmQCm`Pn;Soy1&`lZe@~VjkRW23$5kWS}Tzp!&T|_SCO0K zZz421zk$q*;GUl{i&6lo<%8&q4)cwhLx{zVN_o7FzrKfz`^=;SBQQ*(QrI}$9vKpW zz^n!@uMBfYc6|_2BtQpk4q~Y&$P$zpR1{PZv?vHcz`(FDoqKz6OevWHt3WJ?Z=w^E z5PsOK^_A=h_2lax4{oLGh^>Y_zkF`!Qyl$z-vvfZGkFH%#U?Z&}nEQSj zIl=-33?(=4ipP=Xinydln)=lYfx!aCwTAAq^O`T%VnXwqO|16zbJS zgr+cxmViy;8KX-g^!5at^vC)V*>2^_5ts_ut;j3`>f}q7$^O-`n;};~^p3T}X2ur9 zR>UrXxIsTeYvTAIVhW%Ki7|=ki3N$Ji8Ux>*S|8c74_}tF)0_ER=!7^5HH)N+zn@( zFSWsQ#)~g0;)|8P5PFiQ0?qiinWh85PYDLU{B-KmmnORN8Je`okU$HmrtbMum z{zk_u%W7(t4gJ&NnwrJhfn_MdA2u+MtSZmdKmM)i@W&5@&+j^1-*_Nl=og6(HLrRo zQ7cL~fSvpRz@ey)IHgU@)(dxJnk%1YFGeC`gbAgbM($ZalmW*TL?7D-WFvDjAWB^t z#Lp2U0`oF#UkLL6BST~$#dwf9YcQ~ZgG#(gO)%O@{kBiLYAUN!_QrxPo^@+WI=mwJxuQ3tRip%jg&c4H}er)1a}gvE@mKj?iO z8t!puxG5^xWi(36$5Tk}-NJhB_i@!%Tt%EN{OWx52P0R}*W?4TRJe8IktYbv*~#|` z&6%Q8kHaq&30eIxEMO>;hLeEQ8Zt|l!l+CqL1|SepNnXLSJ6T?=Sss@68um}GEg%O zAJT{`!sY?4=wtS=`8a&ad=~l;SM=HBgOg?Xy7Ftz_%u>-oe36p!>8~kNawl**l{D; zSmR7F=R}%Vo*~kzx3L*8=T_RYD~mEsrp%(sYP0mu(k7|{!N5k}+=zHB zl$8vR(aejs77tFl{ovxI2bv07*L`nq@w&a8w|8vWw!CiR^61NmHT-Q{e z$lATl#gkTamNfq$E%Wh?1>2Vwo>_fc{q1LNuNgbdVU4rTa}3?No}^>)wrzzaK2(|9 z$LHG_)2x(&Oj&jI1PLJWZ#WknL9otolnAOy&{~x%8dkA z-yDkngRW0zu>RHQo4KQQK)NM8GrcIiB7G6kDkzptcIfs}GGuC%(~!&tBJ5HGm8y+s zM^{Ryr4g|a84-mM-izWvbhrH3{YYul3@mA1}1 z+Lo_vQTMHVyDn?ZJje3NtmMo`lH$;Bs;a8?&I66h9%-38x#f{%MfH`F;%4l>t!evq z+C`#Lpe9+$LG(}n1bfqUeF8*f)cy%@Rzf@o&2eU?o88;ZLY~}i2`IgREJTbTlBI*% zS#0zViVeyLDhw(QswJ@za6)b|D9#x37~2@fn6fbo#}J&*O&-w&nUNH?2`ZwK4L=Ta zfqxKgLIM~&3e7TIh*Ca{3#XB*2=7SS3I!JSIFnydbBQZwzyrre4Qpyyy$BXtg0(ne6qIy2H zK0t{JEzRX}yx-(o*=ruhCJ zJX!DhI681oYu<{gT+JDfo0SXEo4TKKT$qxiY^9<;08BFT^Y9!hnCMi(nU(@les59i zx>zCWyb?ROB)LX2FlI|>CLh3L}X9NZqvCo4EMI3u_)xIDNv81z~(uvp4YauJ|S z#85^+@i(H^14?<+n|IgVzin_@UQNSbwl!*Oct+CVY+Jd*YVfUU95{QW!tm2OX753& zl6E{R%hz};G&ClQ%<@Vm&P5j}OeCb$Ol6mD6NCN_vlxUcm;l0+Q*(vc3FIZZ#;)7mY+o?!LBvkQ`6fF4!z&9nYj06&7Pha)Kd}7;N&5218fw zs_$=Xd8|1nr}?p#mM5BXT-B4R^J6vF3@tW)!KC5^_+CA&#TG2kdiH+dT(QFW#ohAz+HOT@FL&oCU4-_o`j_nK4j55H-+4;T=k%*GA#UOeeI%$vM z^>I=UNrX6o@O*+Qo6IJg$zdupEi@5THf=J2Dl43sEfcgY7o2f^R40J8nD$t2%|@Po z62N)4QCA8-x>3{G!s}{g&o>VaMx_-bOk0XmX-$fYD$GlqWVb$E)jZAa%7O*%v>lys zwyxG9hiYc)jhjKmB1f-l74<`>^wc%043-A|6C>0F|G%gZ|}w>>zq#t}E8WzW2-BPHebjnive)^)u5?mc_e&M6g{;lAE{ z!|0n;)4gKt!L-n6HOq6Vx@vQ+))QmnR?S&-$C*1}0VKcnY1U|L;I$b!y_sxj&G4YK zF#ml`A1j#uusI&xo{9e^w8D4iL5L+{NJ30R78bz%W=lIPevscRv6c)=p{3kX3qv3g zXjlMo0<2~u_J9FApDsUo5wf(E3H%HS#0-`O^nbAej714{Lu@LyIk(*2f#fXA2kCmWc5+RXI z!sYAoxcz;S2*(hq2AQp&s_!x6u1eq*S$XL;c*b=dML@?lIvVmC^<|iL=fS!v^gDk! zxB0OR&ByPWGMHFWH+gAiUH46kCnXBNkUC;0t?TWsX3%U{khAmfiw!yRC?cOvD4>#} zoFu^voEIV)EE`cdDl*Gv z5<)Vu@s!4=P%~>b&7qZP3pGLvZIdQco@zSNL`dlB9#y8oj7qw*$T@ci3UKtc4Ig% zcET_srUCjyCPkQ+AQVA(DNP?U9I^nTrN8L$o{ z0>)r#(_Xes&l@|vno>U{X(^lwQPbw%me+W!ZN`kQ zlj~MHJ4??zwr%q2s_d-F<&NgwB;pyME@&MDKJI`u0JV9n57q>B}*f93a||_2ibxgL1jS;p-1&M z25o|R1^EI@Rx(}0-2sdaLGIO8>kq7-IM}(ke0j0On)yOzmVINddHm9yOSQ>EKVQDI zWL0PJy56c;OW*FOe{TOW!Xwb0XK>0Wa^ewaht$11AOyX+`+P!R(g22xsvru(BFIhx z%($Z$CxWjaL+5^8`ydIDb4&njfHQ%=kf@zXB2{WTBd>pIY-&boVQP75Esz(8#1Rx4 z84-w!Q4LE{4b5m(8L8Lyi`E=fJJ*z4=o_rv*$KmrxMod5&22ULS%!hKj$_MT zb^S+z>mju{w7TPP{nqzNAJ5uZ({LbB%WYnJ<#O`jwmWytCjI9N^h`kV6Q4u^5~gfGmv9vgt`Nkco&ED4KT(&uUVQ4WAAIaOq}q|p^)@`Q zTeX^6SD5<7(CVRI5FQgJFxDgcap`qhRU^do*sv-|{#RoeLEW5aGECr(;xB@^5j4Ni zma4uBTWV!6^^%YK(rrt9`AVL!r2<~mh3F~6Q_2M8MBg~Vnm#oInGJ~DL>^WDYTwPi zjQYM7-%Q^k-wNMFz92`IToT!UU*lrNrH?BZS30f+t>OAtj%!87IF_Mw%TaQj zobf1ao2+uu2S~{X+Q8W{m<=|A!%${eXrQxW*o0z4PD>0|3Ctnd6dt7;(L7r4>gGz; zvCW|nBv?Z0XrKxdCKr)q(}9X0#n^n7p%ROHRdv^{uUWyE{HpZ)Q6*U>EAM zL|TQ>hhqjsOA=l2xfNI0DvYbsV4G$cifzz8hL9|6(0ni3m4yYF?aE?L`07(^R~FW0 z(ziHfQys)r-Rg}UY{UB~R>i9Plip(#d^Gt2w1299N){0aBSCLDjcgTz36qRt6f5FE zXQgz@nupvXciu3`B}tyPXtyB%2cNwgqV;+teH(7;}s*#t~B%vk*<0`x|36 zp;t3BASy}G-yGl0Z4v$BW8*X83**bxK-IfGv zw>Eu%BXD!(t(jD9iwm{Jui;#(0g5cDqoi-wYx7%5lPykv|J&89b=3n$T=h7j?wHok zuuCs9G$~%T70@K>J@r7zRqpDG4JCgFxFP&7Nchjwk=n~!C8e^ybnc@0^+jG{M3EPW zu22C0ploxtE!&Y@mc0L)PD9upsqdq1S zGqdp6zPoSDkI9~vb-*p-Ab22NeY_`!Ygd zq3^Ycd?!Rf!8vNvMLPQC2DC>uu?7VhrC0})4LDJE)rr(S0q%J_5!HxHa z!yBN4AZtdPey>CrX=xMyL?zlbeMQv0@bg=!II36w&~!p$vL>#~}J+VM*pBTaqKG zENLM%&TCBC1XTbF17QKeAf%Ja^-}1kRitU^tF^=BpVwHEmsivHN$xWjFFrG@9L*}P z&&!<{lN6K?vRs>?Zf#uYTDwyH;Oz51dmcg4PZ9A63p{Q#-fr|EZ4rMM4iS(#clSlG zU&K$3cqJ=QgozTVjU?vxua4Y|d=0=P(h`{&Srl0jxd>?-WI=)ekK!1zNeP=+PHP-9A<8fc&9se@IJHX>FgLfSC?Z9=)0`jBU3B*%i6FkSz2vTl_r3x7l$XEkj z;v6DHOQEJlgtRQB%B}PAIe6`(j2~r(gXp0EJVo%R2q1O+YS8rpMUcAAsU<#^FP|{p zhVv(*g8$D(b9>iuZPM$w!Wqj3`BM?tc@;Zg>h*n#vdFJd$5mj0muFA1i#`M6E*yKL z$HKskV~vB0#Y>D$Hn>l5%h1kCuYLUS>#hZcX@^9(lCj=(oUj$O=2!DcB~Jp|u|b>u zI|7}gNQyvb>=fJJ6-_D)>L4Dp0StMHg94-nJ!91r;5&Kd!jL!7)E5#83J<)G0o4I1 z_qpH$2m+kR2x}FZQLA)Yz9#J-M&d2t6{M&Ek>B z<`|XXae_U3-kGS?YGb2A5q}Y|$$?avBf%jB$v(3$AsfVOG$RDR4>O0^!W>~`VGF7K zcVpNlv>@osF`Y^#W|7>^)tUX1W0NzI3zN%}Yf+uqc|%Szp(y&L7iom(tbxGe?|@E2 z!Z$S};QIP9jDsQrMUO(Nt&9@OhKEaA_gB{+>8$eqd0=H_>x!EuKDK}Q>dFku`0|{| z^-~d!6UemL@AaC{eHzsYFRhge(vu6F7IQv|CYRQdb zvSv5lq@FpDH7z?PZ^?J(Ph2%;ywUfj+S@%ab|elOr$|9$aSm5!fvf=QqVK3Ymht3j z_%YJ3odieCsrfL}{rC$kq`(f6j8tpxpt?ad4!SxK0D7gt2i11rQ4 zm@k|mTpx{$tk?F@0jHQyd8xJ(_WStwxundF34=0Ak@!-KWF3~I$Po(l|NhAQAmtyQ zKl9%a?4u>hj9}4!N3eTEu)8flH^85lg~I-vU?oCI#!+0+sb~l-9G^=mRWO34#H6IB z6r_}<)F79te`QK5Qmh1b@(KiQ8ZV*Nik6>EVUK{vm8d`&V?IZMWOoPH&TzqG=5? zYHyzr7dP#W+OF=*5epHeKM4C!#H6;mn`-VoSxbplhr2<2@P{)t|B=!4tZ6}N60sjp zp1dfv7o^?#QjkAEa5SfkGu%il4uIsE7rMIda6k%*G#qrT&py$XS)Z4M2183y(p(2$ zSCv0Ov--hZOTr_b3lFYdjKIed?Hs`J-!b(6zlouTPS177gOqB-`G}ll$`9B4Irdb8 z=ta5>R2lSvAw9;+GUFx1OIe+jT0cq%N1)D#P9JVeJagq`Qt1asTM;__mrr=e0g+1m zS8~lD&mh+f)hDThYw!i9C|f7UlGkDJnu@ft^!bpY?F?bN3d?-#UM0V!HJoFJ?75=s z#ULBtVG8=$T3FZB!f5)U`59S(I36FTG)RFZj`K)Vws`rH^@B1D2s{KEuL*gn zoL+f)tO$|zM@c%M$U)=?5+*Cs`|{M)gS~@S@?Yr{FuMFceWiI|uj*MJT_>`uy>={A z>=;$H!;XbxN_ypiYa7!Z)T`sz8?zN6Y}O*MxP*EEr#PgpdE(uCPWP&&$rp?oy6(vApzIs*#l3`JAU z1KAdRVZ|Ih84b88YcY8p47Th^iCZ2F#u4c-?DOX`?IHjU`_G0-z}X{B4LD)d+Q~lq z%7kYw4mQ?6lWJ5;kV3nSY(H4c3%m@&WhA;@m0P#WE4R)FV{8tYC$JPUHeNfuP9U<^ zTIBSvW5$LfWTzU0y=qWcs;-(TpC-3w>i*sdIn+$c_LgW9iHxEgsxSwkBsS-i8CX09 zA%C$p@H8>4By!$KsE)&jafB1WT=5$%ypa72&WNULHlxE>W?X0_*}}NV2rW6uL|&PG zYKzC>NqB5{MtEU(d3Y_HGtRgq^2&fduvRQ0n<BGrFqQ z%t-NZsjAB_ZANqD%oWMW3A!p=yK`OD1am>#bKT1iw9JcD2k%)hf5yf=bI;7$v8JRb zJ8MO%`V1?;`M2J^@7O|00iU@3;8KpL`M_`vHiuA(ID!Y8EKXr;WA&r>K!i)unM8sx zj!Q6*(z2OD$^tAFOQxmBQejy{A!RVFQ~SEFP$aKi9SKDN;gaX;>tcl=yj{D$5cllL zi%3oiHzg+_9?Pn-z(#=~LV$~U<)~SWRVTP!as58>$Sc}Z|7csnSX)d$K#Xl{f-M@o zJ5eQ|XsB+eURyBqyw3cUCIZ9^9jN?FkQ<3x=;|r zn4C%>x|Gt+^(_2TVpB3w3RB8cYN3OjiZOmpT9wn5uhSR4!O%Hj5qff>>a~>9j z8lVLd+hb4xt#!kF+uOQ2-z;f4v@~aOZdPJuQD%KwXW96ooNQ~(yh&Eu&g$jII!a3s zn)GiUtIu@p-`}<6;fJgCec^0~utdZscV|zFjE&4$`RK;FrNyl$TbG~cELY2=zX7J^ z|4aS`8O`D5MqjCTgA$_ProdClO|PnWgNOsY;thR=iZ^FY4t=4%JEf~(a&6rm8!lTi z?PFXYsOB;DOiT8HO4kc&QRMDppA5@}yLyY%)rQ;Rw=AuZbXj9*lm>qu_y3lntd zmE!(Jt9c_QEtNOoy$I?Ar@<7$t)k|Q;mFCMK$e}z-+(7wx4!|wFYq@oX&Bw#Am1;| zd{_A!XeRc_20Jn(uE*VwIIp9(lHed1xjwgk6-VuPoQ7Dg%}a@jysZ)D2wQ|B zqAX$|G8Fq8BQ_y%kz#JH?Wo`*>n8$8-C-H*ozSyP5i}(Eh@rC?38m$o#~U6WEIAzi z!19)zP0bHJ(A>0b!`aynO?q+vvLCdUYDZ?YnIhWCr?08Bt!P+RFukN8ecC`l%7o&! z=7t7aS+mqTvEP4(zd-_k_f&phxe64YH~8=IHy|OTOWT6lB}a$ceS)Z#wfY&1Q@-SP z*Jt?;tMm^nVm||)itF<;NT~b+{0!8{8{v;x?pQhPehI-Atl;nTGsuN7vda%%CtE7= ze`f_Ct3Y2lU%MxuuVe%IYP}p3Gcw~P zdX)i@my@jgawI>Pbp-Z`A|WM^do#o52dxmn1mLc5WN3-ojJoNFyNa{KWyTf7Rm3eq zIdu=UP)?1>m;At*gG@w`%!+iWYwLdStWr~XaseRx3S2$b9BYep#FoV_q{?lLv71m~ z#TgMsathhtqCG`aO5zh7T~`)z2aDUQ79UtYWy<;ki>um;2M5zy?5%tDwAx$J3`eT% z_KGb}u3iJBu+WayvKun9k8a<7EDPdzfR* z8RkNBxw#fK)19UeN?j+h6s!-BNj4|jk{!uq$qV6}?{7@r1fRX0&8R1*AVEh)b)u+( z^m=Sr{KB2B&uuF!Yd=!AbXU^TPj6qFb=%zh3G+7;uiRU(ZSl0yuKo?HpWIS0Z_ajC zRmUD{J^kd9=OG6nWMZ;&xydhA$z9;TN##nUn&h zOie)44r=N5m9ae~JEVkjO8jH|)BOwlOZ{v7Y1{o<{YeERGo&+O33VF+PX z$fgiFbI~DK2RhkNzJW7Me6jPz-kUA}EqiB>=~+Z%OJoaY!}tg~LIl0)z=o3qE=@bW ze(GwIEykP~A08f`iAZ^qsWQ`1U!Iv+Uhl}PG!2GVWEP5#g_#xM;hwjKYvLjdpa8ipj_|6Elgo1bS@BQxr4BkG^38V$Hg7j$*b zc2#b__(G?8WZK4-O7&R#3m3Ny{Nfj=X_Tm%&YbD*M@6GgU6)AKBX0~=^Y?jU|tG)DVyD8@r7{!e*hMxBaVTeyZdh6vth-WUiZ9qX#keE(lj&h!Ykz7w>J zbmZ&v#mMQf5F|gagf2fwTK6@+n1h2?>INSX5V}%FUx}uYF9!1nQ#O3n7ej@0g282A zWDJG57BmH9!P6Tf-|oeFy|zyx>sP%o?&xOr$ymb&9~nG|7Kw5PX&2cUBkEEUtp00! zF>=n*R(!p_m}qG0YU`Vi_xB%m)xsB3KIzRjCzX@1x%0kVyCJUU=aC^r_d1`(eE*Al zF>=CQmI!$)n8;qIFNRmZz8Eaw#aGrm-alAAY0a8RXv~O%S-YfSUeRD0$pD@hgF24hRTT7gt}Gug+ycGIDm-MM?D?L z$K*8`J%1fulO#{Ci3GaW86Dkg@-!UZzCsDmtU}}@L zYv;}fK=+$m4{O`=a(|Ueb@70$+-F<;gzY2934&zVrgwR>q5G?->44NN70QsixOFD- zP*6rm<$7^u{z9TZ<&IccX-d=A#)09|j_;-Rr$QV{C8PcrU-*O(RzThPd?-5A>7S~R zun3Zmtek5kZYaH><_5y<8(MFWx*mf-;Ra$nG&cT7_b-4vk=l1V2+K{d$We;Y4fgJIZORm(QDQ?_R8h= zMT=Q?ZA5yWUg&wq{x1%Fs6V##Bb(|-hg;Zd}e%6 zd`0{s%IF2LklB0OjYp!2R3w-L%5wAkLFVH4CAUtd;6mUgk)S-bnXv)W7he|0sdsrvDrSgS|xV zA*2DzHxhog35>AR-8$2Y{)b8Wk3wtx%~9=WyaYo~RBTj6RAE$kR4w{3(Fqze3jO!W z+H(_qJ#Ei})VuTc+!X#WQ+RNB_z6JaDQk}`EbKiP;;Xv%fO7VL1J{wWCwJA09C+=j zn>G+p_=cQ4p^xbLbPLf=`@HJv;nL#5GD~$UuJ=81x!3g`WZd2d0TuPDaRxCi+)smz0B*nyPkq_qt|tOph!H-6HNwy zB_lns#4Pc99#5xwZ{qIx9(RjwiL_Q25=ZXVyCu@It3B?r-Or+^uHAKTOul!fGA5;W>isBMbx4wA~fAm`=fpo^>kG5 z5;)DNx&s2DAlt`rnTJGOnPDO=4;2gS@&l{C@)&p>bg5`EEte_jeSvBOMF#8rBqAu% ziYlFis-O4282Sbky0Z9(#)f8u7KWCG)`m8P-W7UJ=)MRv*gUF7TO!vEGODPNaHMZ~TSc0uJU{i&!YCh|xQRuTQ(siz{>4q*cS$Z@bVt%hbD)ui!PentJaf zDUp?Zn2_!0u}ONrZcWa!yT7FvrRY3&^akB!1?NxDDDfHPxkw725-EC<&NMtp|A~yY z6GeILMoaQUSczvBCEuIG-%LzOnm9XaRz=oq@m-h0(0bSXL0)rdFX*&t*(9=-q?;s@D0PNC4(keogPw{C^w0TH{<&3y@&pe#Q4 zAIT^RG3Zq_d#IbnqY_&+%gK``UHjcA3k+72H1yS@=O^OneI`FDQX!s>=i=#j$5{R8 z6enedBErRvp{GDmv0Ck5dArm*uFRcBvz+Xg8J2f`WZq0qLth}|4?*a5(qLNS`k5dhx+>~ zCF=4yci%myvlBm_*U5ANv#D3tK?W+-yI`qr^#(H(5EbnYQ3US|kZrhRc6eWq$6f@{ zUZC`Xm|mI{9dpUXk8Ls5Um9w-r0!H_s5{S{J9pyTIY574*kxP}sVh;*P#&W)lQ$rF z;w|(}7WI2iSy4h&1oDC^MhAc1|5EgC$!uovtf@$*wC?Er(LaiQIy!iXGb5Ffdjt>x z@>6M&ax;M^SBA;(`4o$1CDm1?Hq&;~L#7{^erf_m_?7852z_;?_@oi#PD3)555ZbL zp;UlMx^?J#A3qhb&+uaymsfP1STXOu)|!|*!lu@)DX#2VYEPX}SCmrOQEPV{Tkkr# zOFi?P`s(r%UFEs8owJh*>ZaT5OS>wI*3?Xmyd!2o>wU}DsSBQ0pW6uy{@vlT#%jZE z;6*~85(Ma?eiCuCCelQj6>Lqm8a7)Y}>l zt7-FgHcxVG(bBZfTh8yTIl4++qAqQIVp8+Yd5_ZGmjfN^u=jq-RCj+Hl00<<0eg=f zKqoc?iF6vqHh2KB53n$+>t1@vx91CzGU#{ssXj`) zXD>Y#SMdvdsz1&Ykq3$-RM%;L9(v;??X&Y&sCx}+xS?X?XW(-5yO29P&Yt*PIY|i9 zn&TzR^UM8)gY;Hf3ufx0M2yU|&!D?yxpguoso93Ye0#2}IxkkwYop)r3E^{Y$ieK~ zZ$P3o{lFFsjrcoAM2fCjX+q=caV z3?PmjzS02DNj%{B#9j>vu}1z~xNXq9k08P@At1^b;CF*iJ(8hF+!&$Rz&@&<0OK{S zh7a*&aj`sMKc6fP}jk0b%MFhSPzs3Dd{u%i?gRIT0nFa z|EY5m{WqLAf8N#e(o6IdC$Q@?%$0c86#Z1Wj}+>nlDU|=gOOT?qA*79-Qb0=6&VWw z{#wI}HSBKZ`SZu=75)m)U&nsTGzh(q7=vN2A)70dK_!g4j)5f0yeqcBNOH&whG3-l zqfHV)$L9iBT>@Bt+i-(HQ<4$fe?a9Qg2ZnmB)dW3uk7E7kguhuRjzHGVi0u8m2+Y^H_~>r1QMt1SX^7-Qo0)dH|gCRe^&o7KX2;fm&0brZ)f-?{u{lGl}b0{OvKr34|Sb z5wBF&`6PUK0tc9vhI2do1-KV+ShzamX z#soZJYOv=q%3>?+{{F-_?)`=4IDTGx=SmGAOcVnW08_*uCBH8yjDQuu)Cx`^Iypck zBP(vi=CF+bDf35w6!=P{o50B+4R->bz)5141kRyw7YrIqx%mwQ&xw6L9`67G&^tiq zbVi}WlF0$V($^p5xd@#YC&jy{YgMwH-i6a}iI;NRSbZ4?iJG1)c>s>|t`hw;KxqK! z(_f;e0!v_hJ;A|?bb`Zj1DMh!9T|z{(%P8bGC%R!IU%AEKf%5UenL!HMu-sLU?cAiKrBZi>XuU*Y~*~7ydAalb*%AYr5=Po zRLns~8ZY+4BBUft>@KDW1@c)1AvH1;hGN4w?WV+hh~(UAu7VFsbMF0cO9!9~z+ZoX zQY0P79rq?lS8qQ_3DLv`>_BA*LUQ{BJp!ib+@1QY7xgJ&RN*avyd>CtrW1t3kg$+6 z<|$=? ziHFcFgrp&*L?J|xr1?P613lIC8QVV?_oKeIGz$lb{VdGO|ZtP7W{Q|#_mHh zIbcOV0!k8lRe5!c@8J-r(EpV9`)4HDlay*)r9)nqF$m-9^tXnDgo+&|TEe2Ic(<3k z-)kan+!hl7qeMFr`qe<54=1$?XbVvi--|hsKuPm*Fv!7|ffz#g5Ohx04g}`b8er0`4KTkJ~LQB19G@%|K3<)Fv5cZ(d(S*~) z`Bf(FLh)#js0o4c=6ZpzWl2GM&aRdaNq^m&80trurH4Z_tNVdnLtYS)pOC%rfjGDk ztee#dOcCWEIiVCGxR^Ckg?UFu3-Xh}%?wj1s`3Z+dS6YdXs_{PR71c#V2_2-IMTiE zW?o<#ac=>3h(qW5frH>|yFkbUC+CB8+>48-!ygZSj0)+efup+S=f%GHXv2s!J~zN% zc*-@_6{o%jb2yFfk%Z$vJZP*jL_j78P)Jx(VKfiW9FeM#{OwP5{|;>_4h-2kK z1_zZkj2tFIzu`D)<%TIsbiQhMi&(3GBt;wreC`Y|P*WpNuu~!U6lA1`WkGtpXf00` z^TYkz?k0%8Q1COz9@(jgtVbda7oaip|C`J7zIy4s_ujwjP4xwC>bFLH*Jc0XA6?I@ z#a?p@meBBG;?f4le2gEh`rsAv!RjT~5!dG;;f@|WHc<_BeNLwzwyh|`7JTQ2PAUa` zK>@@!v5zs3-@{aw7A8R(B3xB7704z^9k_8CGEJ4j)> zeX1Y0P+x!_f*?iaE~RB8+Z=EjIg-h@5r~d=TP8Q{oTsK8UPVoxRvmS{e#iNJHHO`f zl6~@tX4m7c@2@&qv-f-p)=`PK)Vr}{e4l>+cMG96`OuqueDvrt;0Fj#7;_)c1JG@O zAJNIkqOqpQDUiOWeW13wdd>_C;0(s~Hqs;-dtEhZ)TE6M$-z z8>)IoX~1RTt?YUA?%Hae-p~#+j&FYA#iSI5jf>MsVMmF*DO= z7R)T2Su?Y7=BAn7o4If1(V0)pB)8bi*Jg6emp~tuSb;H&J{&1b=rh-dz3$8KnF1uw zpavto2|2w}Oua=HdW&-4YduxzL#FmhbEU1)QCU{GuyR#pTjlo3hbn(q`O``=wO9V8 z63(}3I?hqPei3?UGs@8Sbb*n*J#T^;T|^Qz#Dq~#5%PZd`PuMA=Ks(w)Z=WjX2{#q zs%gccMe`a86K*P+n2=ahU>`GM>#9k%s`l#C(#b_*lO~r;vF=&-fF);U_tc5jtk?y9 zIhmcCmxMa1&>*z3+G?`pPPC>>v1dibWM^7^O7xd0S)r@vwf5UfI-Y84>+dMh-rMc^bV>Gr+Gwe6YPFZ%*HT{Ia$l*vwW-?TIxvu3 zqekxC_}by+%MZUM>;bnccYz1Y2hPJffIopG?rSqK(C0$^@6XhFZHd8R$Ti$<_=(}N z@kV1Q%GRtke%mL-XRXicz8${L`%U+I-+!F{-vSN>{3_tjfdPSOfs+G|1f350D0oBg zaL6qo_l7(hS{?dG=wMiA*!-|N!=5*Vm>xEL6#hW?7ZFcHyc;<&a&=^X)Yzy^QO`zQ ziuxRJ*~QVbqHl`{ih0~T&iwP(wAh`NV#_0z|B1$^w~pBn7aq4UZa97~%Jj+A{Vhf_|bJe~5(l);qWro5N(skPPm zZEKHppY;jr3F}X-KexVY{h!u%tsh(emTF85OC6J%mO4JQ82xl!PMewbY$}g3FRQ^SUp(3neOhtM{ZpFlk z%ay*B5tZ92U9$pbMbAo}HEventm(5(%ub%2J$vWu1GA6L{$Td!vxn!D%$YT3;hg1j z*3D_3vu)1qIgifyplWK>oT}QY6;1@?y0%Y z%za_*KhOPNb3d5-mwC#(;CbeGf0+Ns{2$JD&VP3Pujl{E{NF8@vEaD{=NJ5D!S5IR zX~ExaHr{NyIqqiL%@c0^30i$tR99CotzJ{zR{fpoUDf-me^A|fOW-Xrw_0VuL-D$uK8BYJvDo3URYSUaKpj}7oM+;s9jY1>7vv{ zs}?=D=)J|+iyyw#dh4yX{^Zs_*X7mSyCinWmL->#{AtPG(C;LyeoTE@{rLKy-FD|~ z&ZP^N{$bgMW$!Luv;4^NKdi`EadO3_hW#sTE1iu~8-KmZxN7C9PnyOxZEsF%9^YKt zT-rRpxvsfswPAJG>Ic>oui3DsW6i+Y$!neKlGkOgD_XaC-O2U->wDIp+F;sHwxRd- z=G#x*{_I9&U?lX7)dsp|jqrbiI+q<@G z-SXjg#(k&bJI`$m-`c$OmG7o}ciXnGZSC8>`@P`rnZMVuJ!1RZ?TfeXzbE{j@7^=i z?cW{QUEO_8_cPtU?*5nV-*tb~{lyOB4%3df9kv}4c1+$;vE!B<%XX~Wao3LT?znHq zBRh`naPD|^$6!x)&m%og-uuqZu$@&q_wIaUSKY2XyPn_mn_ZvY7kXdTeKYQ>yKl>V zNAG*_zW47RbN{6KH{Ac@`#;=0Zujcl-{1X*2jU-?`#|>tzk1+5_GIr_x##Mt`d(AU>;1MVI!b+sD1{3heR0Fn{t590SI zE{*?Cf4SlJ)f(j)$fKdSbP`bFghTxnE}4gV2+ z0&>BTa_KLBJvi(VpYZt|@t=P`cli0?bLeS=d*OfyRywJ}fb#C}75Y6y=zXM!--PoF zzmKMO;(2(QGW=J*rof;5(eP#bbO2B2LQSXV@t5DtZ@Pjj^6y8ulm8-?@1Sy@a)93= zukmcfTk!h|^oIfQ28@bw7I!O{>+4vtf}ilua9F)qBITIsrSNUhdx#82L1C<2oBD`tCe_#He8 zb%^S#i)j=pFR8N2wyX;B9~w=0lJY|J`uW zp+ood(2j}wJ-^~7*p-q0G!t}=+NRI)AYx&A7`g|C;ZLG{(&tLM=kb4dD}R{WbIiT?wLIK>mm6Gx-?Pj~Sjc(N2H{_-F3J)c3k z(|uk1)N{m{@*n<;tRQ%HCFDPPlYl;Gqi+wnb?KFZhT*2&<$&pn6~U55r312lxeG&MwR~0AmowA^bEDV~7%jF%-0S2-chsj3(^N z5Nv0m*xLvt3}d8X!WgB5V~kcJFvei~5|la;W2_Q|(V|3S9HYcwj8n`Q<1t=_4JH<2 zf?~m#sEomwgcEXENyhj$-)09Mv=}Hnt8^$jXXO)aGQ%S*igJQ*) zrKDmUhw(q4m84<3QAx*`qu4NxS28f>qW}7Tpl4tva3xQ<0b{V=M;7{RQ0_Ct#d}@pEOel83QG$;UWFv16R76kwc&@!yr{iUZ>e zr4Zvxr3hoGaudcfr5Iy5#?R0%Y9hu;WfI0&%4CeQl@g3|F#Z{F+*2^lRiUS77{;(x5EBxKg8B& zl-0^YjBAuyjBAxe7}qI_F|Nn>Z^{PcR*bhRbr?4)OEBJ{)MIQ>Zo_yd#t)TNWhur@ z$})^?%5sc%DJw9xWBdR;wi+;YC@V33OKHT|sjR|yx6*{M3*)~k-&UG2ZUOiESLHj( z8jM?&wHUvP@qJ~RvJT_-l=T?5D;qH0quh?MTiJ+l2gW~wkKBRrUZn-&PUTLFyOdUp z_hEcbxnJ3YaktWj@d4#7jC+)Jj1MZCG493q2W6kqf$<^bTNodPZ1D$Wzj8Om0~p^` z9)Yy*F6h~}F+Qqn!FWjd4#vlntr#E2_B^HWdl8`FUw3$ho2hXNWT53ZR zl9V=pz$BR_Lz9^>Nm`1J53i_Dxm@((<9fYb@Nor24X9871w=s+5d;yCrp2P-gSR3m zrT72OntkR>NE+Z`+wXpzeA#E8v-e)Fz4qFBtuuR%a}Cm;J6}b5pYt`O_alA8`Gs>W z(g&QcBYn{M2GUmx}m9xe94$^Vw z2Bce^8ix=>7Sh+AbkqygU-{=9Y~*Xeu(s0=SN7NbM8d?Jkkf87o5A0{>Aw*(!V-C zLHeR|H`2d3KSlZy(qA|)JNF>{yYn-o|8VX_`ik>&q&tw_k8i8rhjgcNKT=FCm~KW2 z^Y(rANnm$Mz;TK~`#$?5usbE-IK}z-F$?LhxmwnDCfC;OvwtiO8waAsgg;Y?}In>@;)CU1m$*BY{nUO9NL0 zt`1xixHWKF;NDiIaLEhdw}lT2mxRm0bHi2P zW5Z41YfBF)Ei0W{X39ckv&s%EJFKjv>=>L_eok4uJWxKnd~W&s%P%g!_sH@Jr@~Ye zSL|PLFpkDJyyD1;c@;Gk$5#wg+*xt=I}h5H|Kc4lBT9iC)%?ta$6xMTiJ(M8qS7v% zpm`%`e#Oi-cbbO-7X>Z`&6fqf2%5hNnr{zEaKGTG!Lx#wmza{ml9G}|C8w2~Q*v&} zg(c624+_r?A07_7G&j74<^y~*_qsG6IStKi8Je9J?|5nFKLj)jynY8>mb3GPonJzn z=S;*_8+Sgl^T3_MJI^3PJ6_oF{Ep{#JiFtWSMS{M^ed0-cxuPZJ8s(X-5r~ET>QlB zCk}kV*}iT2J==e{{g&-lZ$F+b_}TWMZCBub{kB<;&)M3)b;I}-<5!Mf;yB}%jHky_ zO zKl;?K+soD#9)TFr_gqbJ-~l@D{3iLx>2l^Ik9YA%>g9M@Bu$f75$fTa|GeZj4Z#1} z25*B|8)htuZ!T3UjpCxEAWbc3$OiQ`1l`(C-!^z>z{;2e>*(7C*f6J4PWe! z@WHNxAAA@5&1(_Sh{1#JhL70;&-2IdDes1lc@I3aUigvs!ms=~;u=rEzkd)OrA(_aZWJ1N`^*BP#I$ z#37!8XSW$%=vMfhkHX&^hcCJX^1seh!O#8<{Nn53b>D$;dmADWpEgnFK9ghz&08^6 z(6;6}k;}R^%gzsvHNE4yg235lRgTHLaJV_qG$!X%j+uF5a&&3sSYTdw=~$p*Y5SV; zuJEStrschx!b`*bvEH#@g?mLod{ftg@R-xFCV{kbP3hR8t~p*d-qm$%-!V4mD>DyW1*uf$7U#mjx}S8=8QRAn>G<%$C~oeu?sihofbH-_qXNVZx(0b zgGK%akU;Zhxv&kOa49dH!w2Q1<)xsotEqCVVBU(3HO(Ngw5t-zHHPru_1Hmoj9g!k zkMzcZ#V5`$d%H8^m>UZ_PCQ<7MSY#eFL0j~x7gB|b&-P`l<9k*rNDhjlp?{~ly7n=|VXlP+ zf6RUIUg+;lg6*!(p|AccCWtRMhvps3w=am`eu^_iumJh>_}9E33cEg&H;DdTp2-Ig z>HJJ4A4IhEzDzz3_4y4q!U;KL_!^ex^PMxLDw8j84wFkW`68zZM_X&1Vqbn{{qN)< z#6nXTLmM%I5*S_m7)N15aC;EH4I|bW!}E2>b)Zzo+ps(8!kFEv@YjHNE^`t5&quvR zjLZRCv(Oj;Ph&I><39~P;~3-fQ7`Gj+2}$YMQIpu)HI$g#{U57L{N(usuA5?g$V3w z#9WV?q@A{^^x91gIqcOr&81}+Jkf%LeYjrR`i;O(xO~I_aRj`?z(pMVSsA8a$95&t z`MZ!e`(*_EbSz@#3t-{+Hy^NvA^%~Ni~<+AnU9`J;rXt5)%M)HT~b3Wc%xVe(ek^< z%TdmL-LW(tg*O6W@R0!h{m8XLT7E4FBP!4Sp+>HR6bDgG4Vy}H=40hD6H)hmK%_op zTXz7?TEN-h_64D6jR9PH05$32?Zxv^Vyq|z9Dw3Qp(=d}HG$n6RK zDqZQe!6G%Ir%u7V(c*GK+rr-LLtkirhmoUIx3*$>8$r9OJ;)iB{1W1bt2wmfw3VZ- z?u=wKhyBhOkvc@%6n0b2q}0VB*Dk7+R9dLBoQ0`F z=PvS}#B<7mGlGAtDTPY=dX%d?IHuORG?7O;E-4$e*DTZdV}na0S2V=Wamm@D2WeI| zq@S}G^^hYz3>x}zPo8N7EPVEqr9?{<=8#)=1n@bJur0^3-v*>IGG%FY)*>d=4B}u9 z($bI`+XJ~ainQ21(OR@VN-xL8W9 zqhw+(Wu%e)Xz!I$Eobd8O35v$&~+UtWVzPl3WgA=os@-IWVXw4Mjj~H9@Nuyj>VXC zA?bXbaIG$NMm62`wCWG_ko~RFAy=H|X`59>T&JDsIPGeeO4d@M@+I|@EpV6MwE!! zKD9o6y&+VN2)5vO*3s#wgmRy3B}fUR(2BCQoW(%j5V%s^)p_3PUkbW8>h_M(lC_VP zd(PpiwW{~IbgC9<3Hh3;2h>(HVJ=#VId&!fJMqtXw*zg;cCq3u%gA9ZxT4juva!^# zpQwLow`mvHf2{Y~zVwgZ@T9BSlL3b_7ID1`P&m`QKDk@l%CS!U;mk*gTdzXpskUbr zFlo81c4$vp%k8%qYQs~gpGFI{E|x=D7uo~fQ}=UMU;Z(X+situ()wwyl)WzhWvYI# zW5Pemhusm;1Iqk%ntft9Cv~(fl(Sk9@}_InT#ue}04e53&=I5kVrg#$1p5D!8t0yg ztG21=w)$h&9J-pYezj`TQfPyYiw;-6{C=3u_*`pnwqz~xr~S(rigZt$hu)0-Sw2qz zrreqDG*@~y7BCf`wtptGjH;b1vG$j19%8X0KIK|U)}U5%j$#Ql&(0sC&_P`X(U-S1 zboFc3Dt29ATM>$`U^v={ooi*)ShcVImFeV?)*iCGIGq;gSRrhVJIX{ghV#GLUyeSz z0;EoAU)i-}RwBBN-j!yk?dHmkny~>_>Ic_+T(>4YJ+?jD=*o@rQyP{~?bbwV=kG=K zc%KVf=P^Q8Zn(zLXF5~&;f`}JM~*({IHCl&O6B~=@v;GBT(jy})PD8Pa9oSh@{)Gj zYdTh`^%Hemxwri?wVafD`e5zwQ3Jm3D}FV0D2#Y%P|Zf2QY~ny8p` z?ZbP!&fS<`isn!kSv!bT!0r zZAX&o0HM%s6EpFtXGU+HGMdctP@1ewk7we{qhn_}OReisJKO8JGrJB~?Nz$iS8Fq~ zG`?T_f5Q3t`)r~Q`C6Z2Dovw?siwUd|ALa;m50Gq700Z9)TyS}k;$I4x}Y%x^{7;L zbJrERI@TzvrCM97HJNH1&-ErLAfKEict?G(KAx`ga`n@$`ba1BtP)rCKiQfV>U^Y} ziTrE3evBJxNfPxban8-zxE!sWMksVuth%R~nUw)o{+xA~CQzTaw0|i{){ajKopy)R z5+kol8}+f*9WT~fwD>FsN=3H|nQLIqxLK|!XZF!#zIO^Gq)k_h7Ei96$uX_IuBr-fZw}NkA;$<9O2DzR-MN?sPSVR4NRe z`x%3?^Dr&7jj^cDY3GaFRe@?Fb=ztPTasJrMOaN?O_imc$th*_qvgu>w2epAxP47p zsZAE1m3Ho`aXPNEt^8h!f4!Ei;a{=tY97*voX)}4Lsu)JoY00UhdOdO6SMDW9jO8I z?>XYhosKq+QjKb`C$xlCgJW+xZC9w+7Xf&T+FRVTwCezaF%xQMue(ahN}altyZTcd zm|PyOSI%l3vsfmVH2cEZA6r*r4{vVt=t^{9j^0e&gOnKigkvY`-D*!vwsK;9wUX0$ zK?<2#zp*TPp07qdzF!Kv?fC77 zc9fikJI42#@VpE)Ix^*DuH{;dQbKISHzGPwqA=P~k8j?(@Px4Wj(nwiw*vL){ZV6^ zvF}XZXwgSxP0~k7+uS#H#L0SWK`vI|p7~{Xwm5@tC0Y?L-|*4fWM7fePCUL@-^rgB*V9+c)rz~GIGqTwUyllmGlpHB-#D5#oc9in{;wrS_9iOhkwGH+3 zeIp^0DpJcB=}N$8cdyF;l@#cEN|hBo}2dqvJ7JG;CXmO>`0r@qfM+ixuusyYh`n@D0L4~Q> zO{vVd)Z>roC-b60Q7xq!2PFCtFXHrN#Ax0Ym$@VdEl4eT}b(XqhM?zLA z9A%`0nAq36BLy6M@jJ?IUF6Yf#@-Nnw6yr;uqUFAq+@T3Ka!@sCk9Ds_QohA>6$K5 zv}fXwr0k7HBH3zhibj&2iE}1rOui4_J@H6VFx_0mS)6gae`Z9IbWEpdHj-)AGuFEM z*VDF+&cbglCP`W*i|K1@hNXK$WRi4bBZhk>Hc1*LTXk~w-+j?ZQo(fy-(Kjd%#WK| zGTHmXxAuE^q=C^};-{x+b#uB1CEHCNrPPwCXlH-#&NwBhu~BJ%q>|4ki&iT2dq=#I zwCoKLOVY9T#4JfoHlnrXqn4y+?}=NI8pggRMz8l&@-e`}-fnDRB>BM?O!5;&us44kd-bVsk(-OjuO3aV~DU>3d!1k&X%S_o%X5r)wNA{Nk zx1DZzPeFUt`UmQpE`aye2eWG;SD;azf+94*Jl zJgJoV&foF#<~+kAD%DaW3#C@-aOy&X94p7+?1d9>F5PxHQBIOY(kP2%34VdNS(eH& zIa!wD4Bi#eBB#hoX_Ypd0@99iKssfWoQ8AbUcosEzm?UpM!InB!x?fWelzzhIa|(= zm~=~z^h#XTN}u#gLe78-iKdU`~XfF zxlleR{~{lfi{!)d5&5WGESJb8`IuaaUr4@8{!K2IkKMZPEBms{mFxm|uBcgPRrM{=j!B|ny*$ldZ&xkr8`_sY-ZKDl3hArHud z@=JM0ekBjfBl4*HS{{=vGA>(Xn>;SRk?ryXe)#5h@_Ttw{vdypKgplvDS29+k!R&O zd0t+SzsO(ZMfsb&BrnU~m{>bC5aMyv-b9-j35v4mF42T$aPlJ4}f=!h}t!DKq8fNc^JlT>PrwyUbDMXmgC2 zXDZEnv%plDh>4nNQ{y~i7Mfc8%5S}CFvptX%<<*~bD}xPEHaH|u~}l8OtV>PmYI{y za`SGp!nBxE%u3U0+RUk@-E^2vv&x)iR+}}Z%bae`FlU4ENXHxjU8)_9F=`A4v@K#zP7BlD|B|Ng~6^a^)nU zqFbJ!IiXjx{M{yTZqd85aEs2(=8ISO2q|8dbJLfO$CCrGWN)G;)Dr6%9f^kq+)Htb zzutiFCe)$=8gMVc7H}UNKoVN1?UUMmrLReG(svVDsqK?0;ACtlH9V3|4fV$Z&B?w% zJlU7unqfYbVZK$FPq~+wt^K3PzF2y6a3D51GBf4B3$-f^X{DjvhqgHFy9u=`4Qah} zXscoOQq++ZUD0qhU)<>fP(0$h33V!%5%&`8WS5SxOIK-^j%t^#%5XKB;cAs~HR@jS zR;3fkzPwSUGgtX}nmOvf%U_l0`cb_Wta$)OWyiY3~kU;eu z40$ew;Ntj5EN@v1M#?3px}hdNgD-eGiUPow*V&I;kO=cu#D<1q7!rfsy)kpjs98B` z)+C^qE(NA75oqsE<#iBQRCsL@o?05II?o7@o?05IBGl`H6D&ykE&X)MXlGO z)@xDgwW#x`s`F^6^JuB_XsPoc*LjfZJjit(y$8A8E(^utzzE6cZUn!#sADElWpPBR zERIN(#Sy8pI3iURN2JQ)h*ViVB2^xah=(If^FsTqs-d1HBhp+I&0MQ9SJ%4Z`C`3~ zG-s|$^;%PvxkmK5ur_n8(`#*}yf#x_n}JumIJCMi9m8sIgI-p<6HjP^dns6L%}&7v zn+tVm#f|R8l@bg~hSSault`m`x3hBvQ$B@r|?M>DX|5Bn7LP zPW1J=E7@puT`y^kR@Y}*^u*o!nrQWmj-}`ue3)@_$W(cm>P!*H&^lE$HdAji4K`D2 zGj%qzFq5gVaH6*PLW^Rdg`nKyhs=1UlcVFqRxk-zfhaYrE5K$<3N(lkv|^zpS>x0@$T+HRUAP17`+W)nC*|LZ>I zjEpg9_xQ z-tzZ%FXedqB97DlVnf&Z?GGh64|9CZZ#YhTd&91Kvf{Vuzs2!+IOZ00Y~R%N(DHxz zjN@~E!g2iiP3w1T=TbNm$M4!sPuaAsuVZ)dhJVEG%p7n0AD!)8yZhR2ALn?NmE-38 zbLYnO?bmi6eUal69>n!Uoj74$AD4*noW}9|&aQiQ|A+Y#T%YhG4EXJB-5b`QxU{LA z<4yRz?(MGiySMA^70PjZKd#TZYkk*7Gr!fZvS2HE9)?R%#VAT6Ru6+J|4Y`6FDQ7$3?hR;fUZ%J1aQK|ArlKIN+@1e{)u6 z_&6e1&l(ILFDlH==JB6BTeMF9fKL`Hr=Q}#`?B1ucYXdHafHKNf5`FTTjJkyXi&qQR`ok8swA?jrp(U5unzBXJiaiPngX{d8Q6n5~gic77iFo_=vrp~r1> z2id=%Kj_9kzmfejx{dy9{6gF^;hpeK(mx6R;pYVY!%r_hqBaSOQs=iwq&UnJKznxAV;vXzW3P(E%|KK$%sQK3FJHyAWH?F+?VQBe@% z$PkUjB1y!D1>f9!QLZEw74Zg#({3>EmQzn!U)FuArF6#~*|xMdPP->OwcV>+aW9(( zZZGR?@YpRiJ05HDO!8BS)yr#Qoc`LQ*Iw7P=~rVtot&Es zax1vKTtxImcpqENIatm)+S>YY;K$mIdG*V&K52S8)@P3OY_|U6@mI31W((_OWrQGL||^rP5$&Rq5K&O{Kl1L#2|rDU-}ze+vg|m z(KSu)jxVckFY-4AQsT{}^_%>GhJf?9$(NsBV~Q)x&#xYy_4_jA_bJEOZ{pZhx_3YO zAgp)F@A>as;q|V#(;sMF>GiH`4(6AY<>!}|Pw!vcLceHPe8VrmDHGRzEnd?5z$v$K z%eXDd9&v$mE{%?Q9OyyE>lDVp=zP5ktaLWd@bR()W1cfl$_wUI<*m&#tQ+sh+m$D* zi@2<3%MBloZ!YgE2dNWG<*DU)<;CTp@^HC$-;Udn8)7~FtTL)LH?5;|)B(A#iuD~zd zrD^iKrP~&NDio|V+nct=&CRbV&o{;S3zmk3z->oEhaP^YvZlCoux9Ux($JxXn*OlQ zTeEHTimi>IEcuVOZVSrG3M9TzYF*m6O)L$D{i%gZR(O3&8X@lZS4EG=F}Y&rH0EQn z1VqEsM7Le!jyzeWcgbe{V~*#><%8mR{hN@UxE_0v1d#!BHQ3noh77@JTqp>^A{*xI zw21t0sxHu3^LV(gT$i4H&&cKewk_u#Y!`xe|LW32XrSTBN4#NZh&TS>O2a_ao?*o^tjaTr779`@L$DbQ0!S*Ai^A$uA-L)G$D4L8!!zzaajCz3 z+qnnZgusqpUYe}JGyWj|$Iy`c*++OrXyVeZ@QiD}hvYj4$(O`cDV)L{kgPI?5l=G` zb8to`XGG=0pnS|xKFrETBKrWXJWjpdD1l;n$>Rw+?S8?}uP&2M@y>gHU-0rKd7_+e zkl!Eru;7Qn9WS0a{Lq<~@VmyB&OCJZ%!_;@#`GbVAWn!8E}rC^pv>EZk(e;7v3Qcr zs#K1*MhuX+d^R4I9n7}-#cc7ge1F|!oqT^Tzq4-Q4*ni~P`;nPXOh21hRWf&)$*{= z#Z5roVBI+7-aPKjGn#sMH!pO_yPo6=CuS}QKBCZv{Gd1?EX7~U;l@OIIz2InCrz)* z6-W3%$Y-AW2x`R5O)K|R$E@ihC?Mzo{)6ej^fcXZ-!+}CUf;;>NMjIuxTBMo#8>uz zJ~5<7K`ZyBx*t`+F_3T{Qp>zA@ zvK=}%b#C6=;<=%@;kkx&P+Vuz+3rqvrc3F;^s4l=>9o7kcclyKM(49zEGnEW-Ewmm z2MtC(7rRuj-&ziK4)P9%A$`kJSB8d8)(P{{wm$j#efv+`@vhA%bfq*m9(?V<-wd`^ z*loI*BaSBFz}}B1dU_^4-n;jY-|y-9{vY=?Hwwba23ejdY#fe+8|q|Xc`c|H0QKI- z?z3@glv%QgX|vQ}mO70k<8TnC9cUCjD?r=FSZs}i^zjC;*C1vV^OB2Gs71sFErV5&e*MI&Utj;U2Fa%INgIlePWiFmL zR~rlCLr|RA7be&jc;Z~^SvyhP-e+f2x2M|k?8WwwJ#5$HA{YkD8RQ`hux%mpjP9cO zq+Y}iQ-(LoPJX~MQ8WFFSJ+)2PKa~*ZVql=PLa0~FPsv58B zMn-8S(~a)yqCZ0v2W%VmpJ-?~+L3R4#d^=qw)<_a!5>H~`&yUBTWebOJeBd1tK;BU zd&5vT+inilZ!N8C4w(72#^JW2#hoKPS%Z&sHtuYnoV)n8!upN-9@=qcXQ{vS-o{{C zjZbG-65b9Q?j3BIQSgujvaCX(0~22w9W-8nU8iFbgfR__xj7djI%_2UB5_W_#fU}a zoFJA9OhaZHZ|B8I@Xn>28CenrKN`6rU2$KLPxi>6iQ|)^FfC6W=hq3AnUAmOE7EXZQwD#o^<;mjxbsL`de3lv zb$iL)hnB7oUYnH9^eyi_4Vq44E(UN`4!4NwP-YXeGo!P~Fyw-!5HJqf2#!7nV;e`R zBhOLn2sy$IuuYmiQJw2T#(4{!3#Em@g;fjJE+o!dxN9LZy@;uDbBmbi;BXpA_E-dC z5u2G&nJcs_j4?5iy7jSVS|9%VkRg5E&dA%tZN~$Lyc?G{9NJOmEc(#{V5ZDpx!MNy zJU!PE=Wngi!Mh%To8_S00?1_C3=m z?$EXD``tB8zVgC3zJQPaWB`}n3-+gPEY1e}vtQV42}y4eSr zX8A<<{Jwlf`TW%Uy!_()P<}Wcf*)#O1ta+i=L%^>a7ERMwJV6^D|W4jk{l*3>@m0s zG5c1fH`%@-6I@sdM*VU@JmTzp^!$eQ@JK_$!);!>DNxg1 zQq~->58GNdckZ{_!rOZWOU!9y&B5aK#-(QMTPfeY=}_y!g{_A+1-7j#GZ~iDHRp$i znhFYQ_3t9y!1M@X6H%7=Hiw>-~&PJ1QFv_0%T^UOv#U zKb-mNx$Ogc#?#Dk{uZ#Q*xYrxZ+XwNAAwDu?Os}Pq`9VmR;!q?gqzJcn8CzzuU`9wcv$=er-!{nmb3`##$)7R;0V7v zBzFtjnq?`xTfme1FTx0gw zJc#Ofpcm~Y8!f)A&&C$tmTJqh7286#u#Ks7iyjJ2uZmuYgp-3PtmCXTZ9T2EiC64( zo!#vrv!$Z4ZF}Zq(R+~-kIqbiUgG=J-7O7GU90K`JHw4lMax%rwWgo^r^x4nSc_}# zK;Bw0zOQlZ+;^1mt-+AiXxgI!2Nf9K*qwSSs5Dpq*WhNWHKM;b8eg%7q9N_tC%N**iuhm!A=kb7M6vl48OrR-6&^#5#;4aW!ULHtjusYn&y z1_SL6ctQp^hoU0fGem6^`g zlsI`y+yYm|;#;!f%z`z`>AKaMX%tScyuT-uA{AxDWtZHNx}^0R%c_Sqm6=_+uCK{S zS@ZH%ty{eLxd;Dx?W3>m-0|Z_*Dfh+8}v8-_eW2z-S_X4-TtO;9DFh>H*0=HV`0&4 z3vBMBWA4*HV0gfUV`vq0EyL@cF9OF75DlS`2e-^I=Q?Qmv<6X|l@ zv8%%A5BAQS=PL{Np!|y7_1Qc82f|#^6~MqC?MOSNbGcmcO`a6Jxyz6ZU17;4*Phe> z)N@3%o^=x{#v62Qz+!}Pz<+c&n1FW!^nh%K+^26v;;o0xd7~e5Eu6LzX@7uCrWWIDif1xBKndKv9DI~|W zwa5c^lCAnFIJ^-Yev78#Qb30kjlXYdT#$Z+-|}VFXmVn@Iys5yILRqVQc$Xr)=DJP zrCm}~$B{dus5jzKcT}ON%qgBK=ovoIw*N%^-1N=gp4yk+P?@jGp6Hx38w$cJ!h6>* zx7*@!HiQRGRq+Q~pSWu&c{JqVI0`EBkIJ^P-sUBZ_n!%eBg2iy_Y+4DT4}>dnc>5$ zmPCwAs@YEwqxGy6OM?}H*im+tx-`+1_#|!1u`5Gz7)x<--^qH@dxaejT>atI1Fyl$ z`rX(Ty=&{}`+ML1{dDukL}v{rjpqs#{hjguqDZ!j6$Z; z%z;||x039v7w*=(CgmfO^1$rp%)yE>zGe=Rww??TAto1@%?*EvW7AV^?gp>R+I{<` z2$|Kj;LbBIfn=D&Cn~QLa66QH|9M^+D=^%mDER*xj$u|2+0-{>7}88~dgoKG?+4Ea z^U^ne=gNJ#wabWU+9nec1L2bJ-VJbu8PhylDgRF5FA4kieKgU{eBzIvRXL}ytgGG^ zCeAqn&N&6$Hf#OLl({ofYIJ2zuyzn5Ry2(du_7?B0yC84ib&{)^{2b#bwuc?o&3no zDWQ}oJ@W>D3ux04NR<~r_;{eO*Ws)iAHtj_gTgOB#Nj)^YXT@9uld_@o1 zPKpc0RmH81qn#bMD~|1K0|$p8YJg#yeF6E8SI@jS=M~-NyH>WY&bOSN;y?OS{{8pP zt=_pe*xXksN-Blx*?d>sWWF)L^Gzu{DsTpjZPdg*2;&irjM9iTh^2qH(zS&My-g7K zxS5xQ)(+VK@DNtd3}Jgrh6nLhw0=tPV(NFa{{U3X;t?}fque8A z@5Mu~#%lzPnSr(|F#{9LK(Bnnu@9PvU380zG9sRt_|3P|Coq>!3V7*Lafh7GUzt7t zz8Mv2XOChIr_wP-=fw;uW)#si7C6GLMvd%)(GkziwiCbpe)30nFt0oN+>|&vU8{FZ z_ltWWSFa;uQ7=2@sTwYJ*2Ngd<#b7|psUKY)%tDkvm98-qbbxytu=<%KU^X%xIGBFIR7 zk}YVfvaN+ZJlr4G4d`-U6i+|cu{Ckcu_2Q5e$H# z;fD=^Z5&)E6v2m5ohik9oK0$1)BdOLj{EQ5wBDkXn#eP+Sly2p0go$#oatBGu^6l>l$}6&Qv9 zO-aQK9EzG`fE0y5fc$l(Gz~ma-@MP0I@vUt=qmksuz6ofTuPuZuyU|DKgHJ6f4H`( z$C*0WIO%HH)U-b_E~T(OxN>i60pHPdplxwwO)r0HS=$PCV@u)cpx2qYG(6bWI2bOd zsn{uR*)iBqBLxd2=iJIxSX@kt=^EHtE>Y-Y0ck86X*9_fu~@%Kk@0Rj_LGs|W2_$( z=!eggN1)4Q4nw7lmY;7IkBen|_H^?E^qTx8|F869m#WuXnjDM~z^NEUsy2-1qQKbu zlFg(N8uka|V01&^`wmy*-qT&%4pf^{N2f;POpfXmD_bf(aidfGrS9>8)vE{2b&IQ~ z&lhfPDk%wXEfhl-TQBU;L!gR5vj!P6Yr|oZSUX_B5OB|^p68SW=RpfQuRAo;@ZR35 zO@f775vZU)>E__hUflUn+}X_ijY993JI8Dw#+Mp0p*@B%X>>Psl6*1Zyhw=van{IOqywn7 zkf`tw58Q>|Ks)>3F^NXGQL3(axmuE&q#nNQNQ=ZDlNv%V)=T_TQoB6z_|6GllCMro z@K^a)Cnv#S+4vcdCE~Ln<_2q@OXixCxtYOQW^MWGIIwF68lOe%%h8R6T-0e@kj=_@oJ+x|W%9 z@(GN$NvOt12l;_X)jlC4JmIFkPn7V`Xf&$p-bYXg=ow)Izy_H9fh1bw9pw}41YHaK zp|bCt7f;B);$MeB$!CHh7of{HFm`L`E7k45omm5Y6)Ogkmtb}CY1Fw|gHugb%s>84LJ81zukcpA6| z`xoK^m{2y~FV{+c;>$mk_)&RyQh#{@&$@P1@QHt7JCblaLdmX+Yb+m3Gct;B7dJs| ze(_Jg8kbvsB@})t_=H<$ek%MH_vl4VNG0-OW=AIVic73 zjl9t#Wb)x&d7r*terx~7um>&xK|LW(FrM`Q zImYB8HpBK9-)!zPGYw)+HRqX&%^`Ex3=}$p8C;NeZl_y9icFPzEiA3^4)-n?V54an zZsNZM)`<6F#0w-(Bu^a*1zHo~4TiD790NNl5UZZ>C8QY8FAYB1j;EeqAE?PSTHBf% zz85a+c>A83``hzV_Woqv@=M(-dO{ieuJY}7R>aL&)R=YTw8h1n>-W@qbvn;&Az#7q zKv_<0fAh@2{WT4q>MnO?u-4}`33a7A$%hAJCfI(<<^~nY#K<_d$uu%CnrURxtWer_ z8AO+iz6?f}jMR+0jN*(?MmPhSRnTY9%z<*GGIfyZOqEiDsa2_KQ;9sOyHY_OTVg8X z2kZzn6v99^=wXk7tSMyrI$QgEGde#IdO%J9~Hk)3yGg|_aR%Nfvrs-wxLf#G#Vih=7 z7oE@F1lz&+Y8WZ*Q$hvbvUUE^R-)Y+qC5 z>Vfg@El&^LR(Jc5T)XG6yZY|B+Me4y6^qI0C47~9PX}_1R6B|-k~XC;*9@8{J)6ZQ zk>$*ivVvJvS!=Us5?Q;luttJDi)Nal*n(KkFOS8Oo1?`Flqr*TV1ahZjv=TNo|kp? zP;Jxx2A9*ZiPo~)S2`~7d*#z<+lub_-aJ~){({CpacFRSD@{2N;4PCghrRhHhlWmI zIfdwIy0|RvPK9@qv4qLmd>M{F8##+*RUm>6NsJvTIz>qgidEuTk)|i^5@Fsae-#Xe zfQT||=d6Xqew7ryJYyIb#QLn+_IhvEscl}DqwC3+w)LE7PfzTeJnv1=51xf~o~;7to?D*tGyWX-Qf@Jc$tU zeA=S(`{uJPIzM%O-u&YEq50wY*rFL}^O;D~i)($fGkEx^J)5_; z2Lm;G&$PAuPpKao|cuJ{V7jgmiXf@B{%II5r(IWK9_fX`@Z7fjuzov zm{&~?bq4h(r=b^UT|UOM<2CQ*%T4>3b{SMLy1Z5vQm6`@zT89&7soei<@d&Lz%)r zWySD5*{XetBSW{>-L`D?iUS8p!?PWR`Nr-rP3-&^>@Y%XW9OnyA!diYI8_+C!Nif@ zD*KCRt;=kLlIB&$XlYFslgR{tZ+x?>4}cy*^sZD_o~zgua)p8OsQ}-X*qhjTB+;H;<}&_L z*XUq~X|1RCR#okNdP_x5ozGX-Q&G9CzM!Cf8`&L$@EImBqhz4Hvu#L?>R+@WHR$dC zjt$9JN&`0~$iUe)=A;eG(E-yqVU30>y0*+qPngdSn@Fc>TDZ}G?r>$VK+f~=t?kGmu8v7 z*vkw>)fSm)G7<|wQAHEjR_udWg`tb7VxCwmhQu(etw=o57@;FrCK{|}_~wyBA3+XQ z->5y_!8R8&6k!}2Rgen!0uv8T@rCT_!qtOYs%^KzPk%tVyo;oJVclE%^P42$Wt-hw zzo+3h%ryD!{rA|Mn87Jn{3kJkLM>m7d3V~NqbmZ(4l81Ink944TxDJhk8ZrfybF#V zId&e}hMqnTn~x{eljkY+ggjx7k}N?PU78MxK?}9)E>s`@Dr~cDJ$RyK;Jvy%t*4v& z`{VR!EAI}k?rV?|tsVD&x3>GADt9h_Anf^CC}1@89c|bb_VCWH-@Q7mdg-ZUfq>7M z;c{8b?qzM&Yq~>OC4r|3-6p3r&*O8NJrx^Bi(sb1&>}9bO_B7>tkMiewmjOjV^}om z=%`D>FcNZT0P=z+H%lUdcv1Nds>ctfje@DFUBBP=-lO$<*0`*Vm!->fQ+521B=5-5lHRXIGZ-y7-%iZXQYdmJO|T} zQG~JToq9ngG@4*lkUk7k86v(Y-brro@YEj1@>*sSz4X(##` z{^sYIS7)rKHSXbk6BjN3vCHJGlau4)KGI z1&AR_fMsdcgcu6@JH$oO%sqUMbnd)R_$=Q$Idkb5a1^SnSS?kamNTd6Af!km zDVOexR1z}I$vz-UEC`2)^5T&V47dq+Je5+>X7LL2P|?&~ zc2qhVD2!`L8Lst|N=fle=FGrRdDvUiQO=*LFP!i-@TbZ_x($|{E&Qn>RIF8Zl*^3`zKOzmxgHZ@9^57F6Y;VTL5^J_ z7n$RWBr$N)Nx69#3`${$stnR2;3Gp2I*M*9c)_)1(?CuAZBm*$5GwemxiuRH*3{Q{ z()qEw_gR{bZm+Cut?k*D82(nz@;mOld*?`-RQOs{t%hc4BCOgfBB84P7YA?8CV6S=wgPz56M75NKs{+h7Q zSL3tAnL~vGSCuTSBc99wBx&j9cqOUQWWvwo`cuC8GPk>|-dD0d<8uq;Z|OeDSU=q_ z%;k?ufq;a6SjA7}11uNH&;6A$PNsP^eH0ThVv-H(dp6GaaU|7xHkb5mZeK3bx4Egg zdAY^8q1wFcnHpCEjIWRb_@ZO=J;I)xL4WV5#1Af4vmmXffP#-*O@K z&*wmW_h09wuihPAJX#lWTKdlade4_8Lt(|R({&!hhgh*(wqnDyV(R%;!=pIwW9MI2 z&r^(FSz)UER9E=gH6_l*R=#NVc?H4N_v1d5IB(#FISzMcxc02G%V(YKjb8h+=vnyJ z(X%KRQtp79On3C`>)KiIg#Oj&*`MQ?{I%y$u|<||fwrQoc0`kAG1g$riozlUt~kCq zs}DFLEW)hRth}t^tWZ`M@ZzY;LAWBrNUyJGF6C=8fcz#{Y2!$E)@uH6Y~fderlA#a zE^QedJonm7;Zsn8VZ)4;t7lR8#^H_X} zuh!>lstLr!+Y4)azUJznuBB%G#?sP_`)g{4+LtbEAFA;+lzBX54L)B(xyMu90FU@x z$Z2Gx0MX6XqJs=7(xe~TD-hYE7KaYZkKz~lzzB=x2Qftls|=y)Wuz2C&bdj$DRs;? zFK>L_GvT@OvGV(JHdPJWKCpOA%Xq7M(lh$Rr>Sdd`ocrPffuH;{_*6T6*Y&vC2=#6 z_)_no+Lfl0k?iT`@40WD5J-n{$9y~SRiMrbv`8znO|%i_0OiTS2*+#&szbI}BOH)q zl!DMkPUuTu8#y60AupjgA(RkKfNPh-LOhZA$jrWoU|A#|2%z1s>|>*m;j1v`+@yq9 z`m~cWbfK~-(&a9lEVS13>|6Pb2X!wQ+LnZEg{T+zCRP=kUa_%$mG{Vc`4=Z-Nh)*u zC6~@WDZKk{HG7)_d;1EyYnuLTVxqFmndGf1ZhByN!X*_te}^@A{n~0>Ep%lmSIwo2 z`4Sc)7jNuVgBSb|Pqku1?b%hl(dru=jnLsxOQQoSx>&SOM;znC-;k4MIFQsBkTUJb z&4Wkl>ke!Vm|iw*>#iQMCe{u<(Z9lx(oia8CMCCi^E>s84{u-jlA(8B{cv(p?cOK) zm%nETwDvD`7nm*n);-JChy~R{`QDz!mf_}t{P4c!>>c5{+7%7U%EJ3Lw+=4J+u6|k zU`HT7yua1g+1DDbZ7L6jhlk4QOA9mSRt8o!l;pdXV9z|uO^7eU*0ke4@~O!h(2$oK zd4I~%)c5f1HT`wziMDXxqibsS)MX03n*GGjQND(a24s>|3MC+)ajup*8Iw?vjH3`TeQ8zSTX|^VBin zzVNrgGgH=*P=lv3;1&8SKs?pkxU37vhHTN`a1Bo8oPt zi;v{j7uM9H@V%zF%4@MUwA99%vRAfuWFo)CTE6Yno|^9EDT&s`mKu}6U)xmW=FMeW zZ(mp(I9}+k$oDP{1XeaxREH~<7kZW*IXFEk*3_Ij*iul_Szh0^d~r@%zOSUN8_UFn z_?^0+tCBCsEraA4a|;OBk0lB&{46&S=lk@f*v(#RWC6kRinYpiDD37NFhV23*yMJo z8T2$!6bs&3qG<96 zO;zXIdgeji>~C7?Fk9=xmN<{M#ogj97Du{YeZ0Bu@M{A*esUz-aO5Yw?W6bJv1Z@X zotww^)zs`8f9IFK6h_j5jU{y(J^xbRJy{4|%;qL^6gzRj{#dT%glg+ORwiU(dQKzK zg2&88@)EF^egU;1{;PfnE0@Wi>d*5R`$PV)ztg|lf3N?D|D^xC-^^Qt+>cE_Q{*OnbmySt&+Zd=)S_cDIOH8B1rNYr1blIUEYuPMbt zBzkFNhr=mtB@#WgQ%F7>Sii4s&E5?qhd0!3Z*1(Tzx%FNA75AZ@J|MIyhfyXt#{+- zP#s9Jxl3_y0poB0;rPAA_w@1iqHhAB?cR)8L@Uh zvQqW5N#SPeE_64%T3gc4-FwXcgKy6?MOu5-M;!bml!Wh>NBLU0SMbT_`B0Bs$v@Wv zeZp76HiVs{cy?-j39pxUu~SYf;iq)3e7qm|ECTP;rL#B>RduOxo|yPjH28r$Lk{k+ z?qWo*#**|DPa2{yPUGi+X@rlIiu0FIQU4Yy>PhF1nGKlcn9dM~$(otgLfR6~ieM24 z)uxQ{SEwFB7ubKu*9h`ugK}6BguVas?>>>_$RK~pJ0lOC9{hJNir|%&CHP5rcZ(_& z6M-VAxyeinYH}y)*OLg=;5EcE<1r*8OBf*;;e=Tjy5H}(8&xic3Bx|G5hrC4DC)iXL69oP22PU~l}1clu6I6aal=&ACo z1^PMO;n@XHbkt$?7rDr!KR-*NvcJ}c>(hZY}g{3U-=W$ucm@T%O(Ccppd%RQ@`npS(3 zf8F1`%;#Iaefx5sZy7j`a-x*p5s6xVAeMp-A53ZJ^Jg1KPy!X2NUWY%b-jtix2gQ_ zyJ#ixeSQUNB{366SzKHpo<_Bfap3ca$u}mj6m7&FX+L@xG?%SZK5ls~cg7*{@5%Qn z1(0DtZ@*AVAl3Ukq1{LDjp+@ji%{~aAbqH$5iB3{OTuaS)kFNlGZXxKbh~F)XyuJr z6BA)*`}eW$(4z!-%v%(`j*1G}Xw3M~wji^%HP18#`)2V^GHjC!HjE;Pi3lZGV$`Ig z8c;Pj0h8B3%O5~K4=UyDh@_LaNTKZswHV|F3*>jCm%^`a;TvE7kAFfXSs&l&m5f?m z&yVnch~L@$O_F`5VR5|!`I5qAX?+M{2(P7xjq2@l@aV4ybx{tSFdv=`+PqN9CYV-y zQ9uXln>JH{|HyCukdY~zO$#n z)p>8pcwqncKmG3D)I+r|@ZDAA&XK|9w&%eyuYhAHBPvnz!eS(0l%f#I6qF*wm5@=y z)Oiv61Zofki$I&L)6Yo!{tJ>AYMCsS??Q1He<(0H!C&GpPs&xYTp;^+=C`6`gxSmS zoLbiuQ_95D9GS<&u8eo-09g|(Ux(D37S%^yq+%|UCiX=>`04LX{BG)Z5yU2REuSA( z{vijIb3)ovTq1*0s~lpIY&0yMJx^Ph7(@Ll5~4;8b6E2RK7pXW)6_thWh&F+{;F1R(Ac`>WlSKv5-Sx6^% zb785;bjEexnXOJ3F|Xt|=>|%C4-Y!zcf9iZuVhxZrC{Mh zgAU%s5}YdB`m2WKj?XUt;8~sSyrFrczTmkZ7@1C`8ty)QKFc0m%xzS5t(KcIw%AaJ z0lM{B_^H>c8KcNOj`LBjoQ1k~S*}``32I5TAxqfOY1wVL*K)*i(sJHn2CSKN zLw+c8CvF80aPth&Tu(GX6#E5%f$Wl`roqQ*Gd=UNU6#0pHKF6xIjg>ZcW6(e$J{;P zC_UOa)WfgvuDhdHXYsF`|C>}}0e@`9DFH4quWnm1SEtjJ7B0&648o!=ZLXa8v}^I2 zA+s}Qp`+3ybh%4pC%Ou;YW5y|0W7Ud#81=?GR(q+;2m=thz9ATR4IAENmF-TrL0Mp zX;#)`r*pUSUgr_#N#}Vdv%YU|L!w%>h}s^BEw*s~vBt)I^=W9bFhJkx(k{5xwYDC1 zxz@F{JvdjH#qq|6I!b~a4>pAlZz?I-bU1wX-~R30JNo-)jxec9k!UmsiVEbCt}Cku0_yl#n$=B#IVOLK!7^qT0tvIZ#0r(nM37 zk1~%RjJudC57(47ReIyB`As#q^))ZE#+y;;J~~m*$lD+3xpKU%?Kt2;@!B|xrB0yh&{FO; z?xZr?7`c#*31-LW7)>Y@;x~mj7)vlnSj0l>*;`;5R^77p7A)c%<1Nlxq+5cw=wSHW zvJ0n1xx!mWDnLdglL?vJmyGKYOv$OqdCA4eq2zEfOvsG1WV#5ZchSX2k&j%El8Xr9 zM3!BQEb~Qfu|`&tJrQGI&E|RsFc(0IzS3Z{wPh(8{v*r+&P-zr~Zk zWZULMcPanN-ukBQQ9)Q$?)S9;o)@njC3GK1c^aYti?o~=rYAMB$K)D2L3XFM25hNt zFq+{c4H|J;&%!>3hR<>4NIAirs+_ej%*Q)&cEMgB^(q&kPv8Ptfdzd?i^K{nNL`S( zpm;%OK^WPRiwY$I5LG}O=6KW|=Zhqk98)fM3{Dc97NN{%H&~tB$O+o;Sm1cRFUgRI zT%ily?T;`1wpedA%uLT~2vqhomCgLzVnVjyrsX@DD#o(|1EDR*5Go7Y{p0N5K!~!2 zmIiwG-&7RKhv%jvQK+)g&u=Y3<`Aq1?f`g{a{Z`Ng<4n1np0@zTTGZ#OU|cV2r}HP zIFFnSebA4-6|*=bmj~K-#!|cCuC^MjWknpdtmw&@ET)zf!eV%ld_nbHcQ!O_Tir0Q z87(UUm8-U`Pd|C#tZo3^%mn^8mXk(0%Ug}HI$0cETU5=UfC)jNc6h7&@lQ`ZbOjzO zMcLu7Lc6j4Z|JzDhyM;;25Xp=GJD9T%obNn)Xg~2cr5{4-N*)~++d%s1zk)X%1+jd zh%uAatPRFO5O#{rAb%t;f5ZKQWPX4jVNv}ng>t@FplVdgFwW*SD|d;RLyQ5(?ypTq zOZOSeFr+g5%S;t88%mZb$rE|SN`X)+^PAFq$d-wqS?D~{!@NuVYOBJ#?nm>H`j8yZPV^4@f{W@y7-<{$h`MKph-j!a(TsI8j@MX6sfmV> zZuex1$5@k$G=938>cGy62)zhgfL|#d6t94P;wc7=(i()Jfwvfm=0=fs8ujca|I$M; zzyHop1=}Hfw)}~Ib~j(eSNC0-Qa^F84&3Xx>-R!oDC%5baK_QSByg1v83Mc{OdWf0 zM(%CyKlY$3_pTF$FZIdi#47EW(QtyXkyXM-ndNS0E&Uju4UvT12E7 zA*R#%(wLY|OHIp5D^3fgh0~xY<4t67GylWj&a&H3{Om-+3+3q}XwR1mOh6kg0<<7< zI4k8`SHqzE#sdd=PeqM6jlT-ms`eAq1nZ=o*%y1?mONb{SRnU9g3j86{pDBvb@B!BW%&}M=m)G5_DJD%S{HfpC&}iHXpD!?UEiM&TISK*Mht zg<}?i*{Ry!#3XOWMDmz%L1G3bIpL|mLJ`L^A=84lQsO1@{Y=#789P3kybdFotwz6o z_5puf9xh3&=>FDbPif;4w@EjJV$|U!mWu8tI_XT#9Nm;~l8;+G*uM01TH(rk%k=C6 zNE&U<36Rrj|3bE#H2KGP2M1#&qY;`$Nq)jWsq7xZb6g}aSFJup|Wyk_k3@-h3B-5AJx^6PrD~-B#Vy z?77iC{_xuSKngE0yCq)Bb<;%fm|#LzCSZls9tVO8Z9XwzNLx_V?|~U23_;djhS25j zVQpZAE7Q|y(~rX<5w3l3?L+j`Ju|^ToiP-oOZYNJ@{AkJ3<-H8A_Y$c6!fweq@@X z)0u=za0yobk5y4c9m9tj^G#_BvrsG8A(xf>co^DW-g=i*A`^(H+b5fwy7OaF*C&RFiYf}h42ZVtvuZt2_FhCf z==3|;%s!Gn+Qqjj6@8Y3&reL~x^)7T^&zDcJaGt_1(c0r2ZNv_H;6nOEf>d#LP|k? zfBxx;4?XW!t2sHI_o_eSk5WR;=>&eglA3cSQT}!UIHr_u6}p6GrbDCMp)qVBNYbX2 zI!N>X4G8%z*pdlajDxMXhJ!PUj7&#TW{M`m%Fb2;Lab<qGE~v5hm#Y;nhYp ztc*Lj4P3X{f07L?hTGOawymLYl5D;c2X~U7TdpsFh(pIjNl$$cXEl=BB=t%|(qqy; zNZ*sF52ExlG?k0^FGdO&QnTC|`D?NczWS{Jir`|946-}Oy;8m_qE1bw@x&a7ZPTas ziS;uF;`1vS0!1s_LY&#V47E2a-MZsuo73SnC!`=)KES>wnDITAfBgD^H1kXx{<0{* z{xVe!f=Bu1%_lIp~ z!8o4+f9h~v!j4jGml)STi@J@9$!gyfs5_gQfI_fSGyy{?Q1jpFghm8b`a$-&-cr8T z3FI@F8T2p7$ShqNOk1(FEXi&Sb?ir)P)RU7d-=-BoQLaAXPOL|CHjg-PQ>wX(;fNW z=&84+SglU$o{86+s~_D}W3gM3lCAsp4CjYyOKAmfoT)}(*6oH-peTmI%6X!Ia$ckG zjn}jLKym`T5iTP>6IrPx$7tJcf%ybV!AH=(sElD(4kWI)~&WYJkyJk?xS?)Q8A z4Fy|Q;(OU*_FeZzYnLbAQ&GISg?%E2jSuLO#r?VqOgoH;Mvh@O1dFxLT=F2Kn*5KX zAF_2OF`_qss=z<=gnx_~De;GfpjLPX#mLC>FKjtf-Lrj%?%>c+Z%YgP_uD~BgwA!h|e+bN3SQ+Yv1e- zn)0Ig^SXBzeXZQ>F8|shEss7It-z!ruoTZ#$Om}Dxq+dmC{fHdVs->2${UOzxL)&o z{QO(d?Dn{`UNzep^$G%S({Z{|a0%g;DI5%c(3CWmj7DT3eA?=mdX2i zdzWvThc~$_s{p+#z@sl~Jc_^8Ohi8D^f-lzYuu(+_<2jkD}33;sn<5qc&&I-3H2kP z@uETpe2oPrF4bHicEF57Sc({n8YmSf9y?avf893i~JguTavb&wX#>fKad<%{=$ za#Zd^-j8Vh?9{^+@Blh4p-StscgP~)_MnS&;kCmURkC6+Feir=0!9cH0)s&Fn&+PC zl0W55urVIx`gP5^qww$j@OL9l;Gzs#v-rk*lm;|5*Wh7X1SlSYklrf;#x*jSz9=XK z8KmJ1I{6!N^2FEXCfIG&{+7ed-#m4y{ZR9Pv^bl4>zxlb??2hFZ}FiP`CMXBkcxPl z$`UQs;+Ex=_$HnzeXyb7U}Few3t;YnAhR7DyaZu9NsV)pB8_79zhJG1~}&4YX$)=ry zX|sI&0^NMf)HI7>N3#n31U|!>_g;+5R}%A?vi?dQj$J;o{{I55z7V^Xf|pU@%Xod- zeSKNQKQR(O`dmAXsoNm6sROtgNzjxun=9K6#GAYU)g8zRq3AQAnGO__vsc!kXA`;; zA;ZY8#6bdCxV-rPE-47 zp|t+(-?OgJ^mZ%<7e-#(1||N2;0x?1>>_4-@pfP(FuwqSAkFTXD-T_kpShD?efgm) z{3_vn1nB!R8*1?yt=Hs}ERpLIf~{@Pwe5<~$5FP9k zNwA2dY&c|M)?$qOpm6skF#DDmi0Qlk~iqB@~-t# zblSVi%i0&S{4PpkaZ*t!!CDAJQ`(ngoR#gxH||3|PJ2t3|+CY{f2>{@YS=QFge=!*6} z0~N^oylN-{6%=pvpH**_)e!0~;Cv2EA!b;g^`$nI?)EC&d`G(Xdep;nAb<9JOkMC|0aYrCil-XT~NFaPipC-?>Z$ znN88R2o#zVt1D*qph}gjPj6Ma_C^6RLWL{BTw3i57$J24TnK)Cf{WxSBV0qf|%bzBLa`n$6x%Dw+Z<-hAS zmde%t!njo#$@P1``6c&$liize6{mr&_ONlYt6^1Kx%RFe{a?YuZr~UXa0K5+<(?)M z=0nlW*A8NWa~hP({zj9{|NnK=BTq}SkZN$|K_7if(MT*iN27<9 zFTzN`bT)}eqM-)bv-WffaEmH$r1RuDbEVv1ZdLADly{GJ6nT?9t2BklA>Mx4Lc+l2G1i&qoF~l-&a0ZYb{?_Lyj}AETcbq$tWvYsS^s9%7x`qwMV-(;)L-W9?S_RNPVNxyUNGLyWV2HtTBQ zEahsAvsi~lrn{A^70y!bO`N4%t#OuewF~E!s})K63Azy@Uq-#!z`c#reg=T1uSt$9NJT)}ihpbrSTqG?DE~;9z7G3to zI~MIi^Zij<$|BOY=thT^`;KqU>%+@@p=R?^^YZeF^Fn!HyyJH?Co@kmDkv|HtWY#; zA}8PmArbALrSyU1r$2l0J6ZqPUXZ#LYHsxT=|A5RGKLQT*FlGBD@(S&g5}?K{X8Tv zNsaT&(sFX@I}5ec2}Vb@mo;%5Lt!oD3OR8EwxFU5xmRa-NK9bPEc1$7C)Dc^0SiMEa_U#OxMvXSj5N~ zyySc44VE24vm*VGrqwINV9z%VjIvp25`db6pw-it^uMGGmw3#{f;qwV!VFk);%Ev- z?`(i+HLC%kstzzSvaDj;V_rJ#Ih`?;`Lt)e{DvleZ$Rdsk>8+Be#lV(y07M%De)6E z*NmN4Ix{H!8d$Fc7Hv`NZ{=!LJFxS>ys@K{tC@CS_fYI_SZltzreQ~N`R<~BX2xBL8YgHt+8pTChebwuM=B8c=>iOGe zpQjm#co!1JPS>k5;z|)wK+jj4rK_X!#6AAs=(R?KV7AB)DEHPKj9!iNyob&+3T(Q5 zJ@*3l8FK5{Sair|Q}1r%UZ9tKgJSz|e|j%df`+RxifK%%5lMUZ6LOUyH?`ijbJ&qq zA%_GIVptUSD(`OEcFr>~FtzA6PXZoyRY>=L12=J(~_FO%D8iv)gSXp|JJG0<-G7H|W@pg=+ zjM4H@IMhTi;HWu)wB2YD`#?4dlPQX|BT{uay?V;)m;Z3}eEXBd+rvBDKZw4Cs-J|& z6Vm5IpH}l*OK&9GPJ0WBx4cl?FMrk_D8hUE;HdS1Z&aZIoj1a+HyQd=l``d31xLH$1J-s-X(u7Zhp|pa2I2+CdBtX^NEbP)rRg ztLml@tl9YSs4bq}--Hmy*&uOMurJ8CDwrC~3l;}M!Eg{F^;;88i9P6^n2m2)j#W-;F3+=XgQ>YM_9>q}p57E3;qZ>vBx zx*|nilx0*C57Fm`THji#JNGQRPB`7&mqP!EF1VqMtGp(y6<>;BRq=CrDq42KKjDRv(jV!Ab`9O%W!b8KVp*}r=_c5g? z7*{ltReg~c)r|W^E>f?&%&R`)iPR}C^lHZ|zUm@EsXTzr41;IjpyIt=@_as9hEPFe z11LhhgR^g%mm4GQV_=$Sa}%?X9Ci3#s1;Y?IOOZ&wNa?lhOLVfp)c=w4HI64yb&)( zf&o-I>nKd=(1gZ71BlVC!gDU?qOY(N?JO)^k}jEIy9?)^nNZpbPn^l8{=%?_C_hW7 z{!#L_w5lM=5m!95Mz*?&r>10ck!Db?)-(g_RmU`ga<$^ADOZzbP_EWAgVNcKa6IK| zrWus}jz*O7s8|17)eOqj)NxU{`sbt>*wu<2QSPnj5gNCmN0h6fN5Ew+74>sdT>oZX zU<_8P2?$Cth9s~%?UFrcud=VTlO(Y3va|RdA%8)Jm0?k+m^tdIz!#*|5hQqniN5%o zy|Zmp8h?=dLM1II@Wq$6O`jkCD4(H%$k%76;0UaY&C$`va9#{N5aT086LV>6F)0W` z19mGJLgaB_ddbdYDLI&2mAn=u$>SZ#yHKed^&Pc#&@@HG;=mW0oCa{-q4tLU#;ktP zvk^|#Ct6r`y>T?n3F`+;NB1a`6U8JCG;{~qRFnx%Ix@`o?q_VZgE+f|UA<)X)oO(V zUEOs3YK81Pr5jb3I<>d(#036g3|HncYx;`i8fphxv|x5K4EAO-k}3p5+aU8i zQHd9myU)vC`nDRhY>ItR4fFz10d*HmiGq zMJ#SXD913&0yZKytZUJ=v#Y2IGlO;Q5oe|Rp=1DR1rd`uOo6B+mbi`12S{YbjY4Jq z3~s1vL7^pINOuKY+OX;a&}upSAm$`0J_^ngmw0gA&G*jyFZ@Q};a}eLJl|GXu)d%e)@91 zBxKWzndRp(85I-JU(%vl{|nMHE@s@cvck1X7~g9cUo!9yV5=z@7L~$AEUeA*Y#>C_ z+)Np22bw^o5X7u%4k&SGaDb{PM`3RwOd(6vMlV%_0J_0u&FO`JP3&pgk) z=bn4+`MT$xyDQWr7vnU-uW%aSwkwO-09|z?p}b7=kFOnvE`GzZQ9(hH>qL^+a?_cm>!PIRt(`4R(+L|16m_k|P;Z%5KMsy!8+kgL`WV6pQwXxaNd%-q;6P*26_7O)=K^Ed2C-Vf z!72)oq=+8uh~@D;!$+z%ARE)$wh9~+yngC~wTU6YYik?Jwt7d5DKK~Dcbbdy#*7PW zYFYSa|N51#@-{G~Fp5Ny#yJ*&g&V<=8wb%WbP}CjE#gy%;!h!}~1(F?(7{GEwom17DtYzo1HLvsXPNFY?>Hq5~#^a}MyDr^{)jHAdK(zB;KI4*e64oDkyq60v=+jxmhCyKd zUp)_A=+FT2OYCSU)?h?E7T3+Wqp^ zlxx90=V^}p|Ly#F72Bst+)fKQ9U$d%j_0D&3GLgS`v6Nbv>6X?w37wu1$Gx;FDBNN z#PSk;wJ+CTfd)=nq%&Y=TBwX}m>LnC$UYp^k%@@=Q`QrP&K%XQoDSjygD>R&a8cx#L2;g4x;e%+@opC=1c6d*!?hf`)=HC^sC24p^dD2|ppEdVt8Y z+IRG*AGwWbCnZugbNPzpUP#zr+Mdi}WfA*r`}noLi8I|V2GuV4Tk+qPJgD08+%xPv z_ow+m?wB<%qSfy+;~w+GpqIXgon=hLWi?#TDWdU&B1xhU`k0e0Nm>{=PWZN9=w9FL zM094Yq2(X@N!6Dz>{Tw#t%A|#mL@(81j@N`#hb-K#A!Q z!i$|sSTc`+V-M+bR38MEw(cx7y=>b1KrFJe?I<<=Nnf;ba?shp)Pm-+R_nRo6uh@6 zTW2Fvqi;utL6)8=-*Ib^-;V8mQ&;UQuihGIJream&AbPql$6M=q;tSlV7iv;$wPvl zi!CEPT9^nL5fNrI&_CAG%_Ho63lpn4E?m`RRmWv{+%Z0Z8hdao1_qQ$2j7s-A_OIN zz@WSsUea=3Nk&)3t_?{QT@~TRyggle$`*%LysRo&)zO;gW$Tc$`r9tahi$1VEk;$q zmGj27Rq|o-Hql&S`$Xk+unc<6B;kGuL_W1D71}Tc>+cwRiMyDU z@hGlp_*E%`*2>t`Uprkz>=9YD4vU1$>s zk+iTlIk|XYQc^9w*Cv&>tt(todyJBc9<8k_yrT^~onqEMh2-mxwIBNm0CRVV*}+7F z2vZlrhR^rMPyM)lzYe$V4$;d5ju0-AUZc> z)`W<#E40iEgQPUUQA-j7M>^mGoq`0EsDkDFCCv%tFU$S`6>ImD&fZuQ;T!y5%DL;xx@mn$pj=>JT>;c z$b2$kv&cyQWYv4I3)bwc%6&Qa!83E4_GPMeHC?*+_l34yav9F`ir6f-oC2*AjjHxy zBw{cU68Yf0TcYe~CYGi5PST64=-z zgd~^}k`uBMrYF$YBrH!LH>J}-73^dX3Qoi!17cG>a1CIm+sn}E!rfl=_F^$*Xxg{vDw6b=SeE*^|W#)wi$(6Zg zL+mg0;qkHY`S~RcTdQaPvVQ#d`d`jYDbF1jp7GGl^Vfns%Y2SwE}F?tLcSa3F%Z}m zeYc+-0#(bCR4vbDPu6wMP|v}hqdapwOFVhku~i4tvVTZ_Q~%`t+5M;Yr>^&3-XCNc z8$em2?D`l#p_+9RO9O-h=By2|uO5H_4jL5iHEZ4eimjH|{fW;kzQ@D8eE!kcWAhu` zy;_&e`IYI;;&*pc?Oc@L<8x<4MZU5twIkTSVCg)>ZrM|Z1RYS4vO$s8_VNNY4JfSO@4L9(^n>`o?Mo<1@YC&Ub9)F z-s9sFY({{yN-|^K(Wx~7&}AHUyNipNks@Z^!59tBy&y&Z+A9F$TfTPQF{b}u!xD1V zGtM5Yktt5gK*oSf%*{a7fNFPC^<9USF58f*W7x@KbiJyWLKQ$JC4GGo#D?CkZ6 zu(d&!ARCqa;AlFbvAOupg(=&HXl)IhB%rvni4ud&Bb1C8@i2ULDEo^G8<&UHx0&Qs zclnrOgNOR~AZ3)&ztcK@Vyu@tXdITp_Z>%tRv+wsLzRe;^k=7uaTl?Ych4a?rd!nhgjK}Aw3%4`cyDe(ci zvWQ$HHk=*1e4B1ZjPkxURrS=&Xxjkamg~UcBo!SgZPBndES6frwY0mAB6nykNzg*Y z;{nO`1*&AYK50!(Nir-e&dczsgmmp{@zR7RMy0icEa}?p9usXedW4Kgj7p9R+L2$A zo@9e5Xg!paW7e%+pafWp<4rzo9=;)QI7Np}H&vZMZ@q-BW`t`KwA2jK+Nt;qz{sv8=NJ zTHOiov@WLE{(vWfA7WX4=|u3*5OyN?&g!W5y?t3Sfu0k=OaIjq!3T=78kjH?i28^2AKZUb|D66M&=~}~#*AUv)8W2hS}0Nk zuvKf-uCVG&zbngM5Lk8vH?tHN-zJPR1>|fb{leRjrkJ5hXl#A)X6a>*Xv8~~N!bn&NQA|p*lC4Zv zhyf_e6}X~cdR%*#6I35p#lXbW2C_4>=81yNcku*ed^7L)q9#UGFI)cU0_+6 z)%mM6;}_(|nR6?XYufbSWfXx!CnER@Ugly4^h6{we~`rk2b+O=V6y1Qoo1_Q>U7jJH zCeLKgY|rV?fZMA*mqRgz$f8}Jb`B3UW8_FgR%xK)%L~dkl|}@u=xPpLFf+TZFwrYS z2|E>}4hy__^U;uD6&ov*@zy_9R;12bIi|KPe{#i{mhxvFnoT~yQ=rsSpj29M>+7T_ zF%K<~{TL#kvJNEKIln_mM%2M!)MYGBU5l7Vb~u~j!oiwVa0Csc1J;UTb3$o)X7 zo>tkBe``TRV4Ur%LwQ|!MJsdtgZkgUpe(gG0Ir~>;MHm6tHMK*6U~0Aj_j5_l_zcg zjI=!<*Z35*>?~jXUe*rt#^S22QA$G1qAQmSJDVEr%EOGyK`gsQ^%PbZup8YT+2SIX zgl+^~fyFG_LzlJu8xLIuDUwn^d-D_J07bLG)^T3`;Lr#m@b#;|4j)0+j4t|QGb}kkY0OD1OB@yHt#enqcX?<${Col@l-5rwDAStO6-+9Q(FFR& zCXR{PACVBN_m0#gmL}#XAK89)p=EPoW8>oClj2)9Y#d*O$vv;Ia9&2!o~rR1H?+o2 z8os!(F>!Ot1$i3zsK{gVl-n%fF+#S`3|wWi&uu2IGLI3iro$7(fTLer&H9(2UKFkbnb1 z2MiuCYCz6_66}Gb@QCPF+l-u-Ikq;jU}=mW&Z)BRYy?RfW0ee3XDQ#{l+kao=KFkM$U(&eUgARHOmWF^*>TgM z+qGB6Er-fym;EGT+-RdbIpK_uE+it!P{S5a(x4H`rrI-P>4x2v>mHk;8W1pMYEA2d zCFW`AW|g6{yi?mRG-P^a?(&)GA+l|-P4-d^Da&6~JUvt1M5$6~m38`_`)`{$A~-JH zyw#R5aE>`HC3WHY$(>WSEX|l&cn4_*Eb0_JPmylpv|+-an*?nb@x>^ZseYCGhR>IwG1*+EVLaX$g}aC!<&OjS8XdS zTNCqd1T~j#tQ?V^_uWI8Au-`?{_n3&JA1XU3g$eA*)Ax|VLvJJpw=5(BKc^!MCJ;6 z&Z1@383j;)0Q)*>3+See7WQapzMHE#$@azR{m=D#QC+h*qaw@fv#(3O@U`u;C!fe_ zT%BCjoUSrSitPi;lyJb|j@Xh)4ucDk^sosq5C;nW9-R-)J;;In|$w3(&QC_oy-%v8-)zx!ti{{E7J$>}qqX-#$f>?yF$6mF1iP{a? z2gM;#&-|ek74V)h%Tka(;{xJ~P?6YNaHIrLQ%)O1L{liVjwSek`5#K_lq|swv?cO4 z*?ZyWS|yU)EZx1dYpDbLmAh|Mo)Eo)CH$Gcuhe(S!yw~RI zBFvP*D@<1M`7u`R$tV6M#lejWHN=ho(!-6xrFVeF@-;2EPLaY|>;vRny32N$t=4+t z`~*tQM^wf1DlsZ9-cFYx606Z{iJ!s=4WOF<1AGv!bHKj3q)Hi+{{5* zDw}oVq_eL*7aME)_>`@XmfUSS0v9bkTYPru1L|_yULq>w4qU*d2FQug)c>2~-Ux$^ zxHp&B8sVU$9Sgu5Lm7o507Wjr2ugRU?Xc=;)Z>fDO6bu%rt&IcV`?l(K#CgSIXw_k zf{iLTz-YYUPn-!-o%j=})!yWbDG(NK|LoPb@ct_mK zM`gdWa{A>*Y+5`O8>t{&?SHe9zo9oq*~sN z%k*LtC>nKH3lg1*_+qnHNE{m^KG-It#D`%Ba)^8Lod%a+dQqsp-F9B0F=(?An&x{j$ zL*k*r#0#Junp}t`n36ZS#gl>pT@9RwGR!RSXVsCOY1pkV*L1dQSuZxZu&H`qs(0BSsW2T-+I+G$md^k?yz0?%=T>ui zTyWy7pHCe-@5W(j&FGRPPW04I#K5^o4mN~lVsvCs7 za0z(obD@(KF%g>FUK7!bP-Li?5up)-BSuB!M3f*x*%IhWsZEI`BSIN0*(vcqv1HS7 zL@Z+}F%tlhj+~Wp``I4D!Gj-RI`55Rrt|K?{t-35>RMwiN;j(w9rHWAG;t*pOIFWL z3-I@do>Q`FZ+4HtOd*imT=pN=R5rT!&iy46sk{>*Mm_r?zxU`pt`UPnIZkkQ;nyyB zc4)fDEd%`Z<-E1{sfl|NN8*cioE;@Y{>tinYiriCO|hl%MW)P%vc%C_Pc4*JLmB?{ zidlaC&-i&2l_IbtRXGe@@ux^V+UE)rkM_B;91j+MzUs@58N`|clBak$GN;h?9~F<> zl#%g;LW+6j6dCff*j8-H%{!KBvXz>a%9Rh6BY(cBDCfCC6T%RxY`Y$6>_AT8)7TJu zT0Y*{i5;;9lu0%d(+?e%Q8LeF1Iixzbs59%z%fcG^ zw1YHKZVz341oI#jS|XR1XpQt@7ix5AO4yw^Hxj0?#hY20OkdzD7c7}OBf{i!*sYkF zU*7>w9;D)`QO~v9-4YAq_t{18-^n{KeV~*#ukCymk`R-T<+fpV;8pFrDE&-f5vmLm zRiv@Q=%taP)B7m#EPx-k2nCtx2@K`@ydNje0C@*62u-Z%PZ=cVch`1g3B;>!=B5F~b0Oq{WiX{&yLOA< z>M=l1?Y1KH6D;L8Y(=t69DWFgt!R-Omu`Q0Z*1)4!?yd+?^Z?!78dWsA9gEn=YzW3 z8so4N#TZb$HqYDFo8*|6L7-lc93vOu=7+sP8T@#_0fvGcBQy?9vD`2QjLQ7jZ{X7b zUX~_2wdbG0FQSBDq`KraWWWNzV+=IaCG3Xaxriylt5Y<&%XARn;8;>u2*`YPg+R4M z*v?%`U`hW|SkJv0Ci*x7+oPL#k?q!#o*d``1w-&<2#tvsrHQjhL-Zu*hOq5UVAM!l zZc-vfRu%^9_kwhj~%*vl)p2 z-T?z@esyYH$07#-PPQs91p=ku~89833TeDI%Ggpa{MsR(itViT4pU* z3av+#4few(7ugS=L@#MyC~-gX4%qhvx^S5us{kDnu0f0BK@m?B|Ah3qpmkc29GGaJ zSbi`cFheBK!f6~!a~tzQ{Y`S%DS1D*;No&yDx#lv1uw3DyZG(;hm>%Sa3ru?w?mBZ>gfSQ4}|xXO~6QyVkD8Go7R2;JoXsiELH3pyLRLp z4$GxYC?Hj1h|z>>`Ps(l*nQGoZCs8`DVFfaUT4CqSQOe-DS<_%Q#T4uLkJ{`jEui# z65v0HJi+pEmkhwNS6|70d0t?nQ{rERW+nW(@mP%=DNZ|cS>0in5tDQTFf)QMa|6re zIO2Lsk8M~8il*5a-HfxXF*~C}qX$Qiiq45H!I9Sog4JJ=qH8gYu+LI1@o3puW8Ymz zp~v=kP`2fz&tWm_#3!&2f*QG+MMr^m0)j>jPk#`x00 z%#hKK6wUeN9DQiW{QD18ezHe?YSlvzt!jPn!TkHbwp96t`42Fxi%Sm(4Tzun@Y1r1 zF^&5hEBCF;k#p?6zIHg6bZo`ld5^1D4s`IztM<4eEerTI=)umndFFbs9s3?vtYO*r zxYDPQs>blw_FKPxPJTDFr8HLOv$S$a@gaAAV^X;7qd{_Tcv5^+ghzI+?O3dwl3ONA zt6EjN0+K57vX+GF>gBPKF+rM6ZD5RTS3<(E;Y{PfETqT@hp&&xKLNK;f|-X~3mEwx z*Db307;D+Dx>URNTRmvjkf*N>we;=j6SWDWzn7cv1NBY3fwqc622BPl#iDctUG z^p$y;#*|Kwi<;Xzq6%8tT3)jTb2p#ry_CB*G#PNhHZsjZeo&^_u%6ggm$(eUSq|#I z{lr>|d%3om1%!ym5sheUAc13XH?x91-JeRaC&~P-?>|!>>gZN}CZ_%sgX{ zeI5DUX1bmik`uqKVI3D(WLWl%9*$@*EFZ9cI3p=k%Q7ZMn(@G z(^OMcWy-Fx&m>SCo%&_p59>0M*f5BW3->7-lk55dp~%X{EnREni?aIYE1Rt6KSPM+ zDy8*GnX=nj%2(o5uTds4`2swF8p`zuy#Fw$?T`Z8*Un@=z6YevixXXm)vSwh4v6+W#@<|F%JU}nP$+@^zM;5@lyR+-E`zX1*YiQPbouA3xpC&YqR`-w>UDcd^J^1&pAVwig_AWW zwLZRJAx?cCm4xF#>SE(|Bh!F67IY{iXTcXOF8;Ax$=!+i+-^BCei&%lz>K{L;mEWAY{G;l}A zkxK^l!T4pcfQ1dXAYqY#oq;?Yw+)91PdV0Aw24jCj5t9IxGGdNST#zOqbfo8Af;*d zW>L=GA>JnMWbbV6>E1Nb-pjp-kb}I5S4KxsaG@oJza_DYZKGcWnY3k)^T-~s<#0J; zld(gcLZB&ab9HF+m<0i}?8Aa(x!oi0a5sho$D7Ro{+_;u;E=e`m1UttvB?GTO8@YJ zirVDZ)P7}Mv%-2`TIp^G2~MELJbaA7A?8r!cufDuoaU0!rrgN>F^CaqH=D<|Jkiqf zMDsYaS$^C$8F|Z^>3hp~v)PuXwnEVYGTLZCEC5CA;_)=CndE{4Vt@T*Cd=hqXKa*O*xdvYj{Q}B#@EQ zpyVxGh2$AMWK8Hf`br5oMsJCQh-|4X>U{LWD`oO73VFmBQUnu?p{rk1w9aI(_`;Mo z@kMZytNo%(J}1gAcWvJWadV~YIeg&#rQ;@9pOLxyu-b@LeJPu}vsYA9e{FONO6tQa z3WQT)MfL(BDyd~)9AIqkgi}-?oe)ok6nvt{{iXtN(2rbwq5=%zWBRT>Q7x?VO1Uxb zjUD$#MVx%ZR`T#}rQT!Q_%~wT7+**l%EnE1ZHAsUB9Tl|6`MifX#Z-DsDQx5m=mPN zDAM5(?d^}GfVUohB!L7a&8QR-KrbBa%3HAG{!anZg4hM)3+0ntN4w;xyEZf6HWOIN zp(|x%=aODcxasW=6_5#FuJVWS_U+j0CT$AMiavvG5AcFp!`ujSaqdHrLl>>8XsnsFp; zQsw+r*i5^^7VUhwvEldG>&)w_<~uMV?UY~>nPAbkd>2( zL+k@dq{uqEQxhWrr)KiQ_e4aUd_;*j`pV=YTdnQy@?7unpV&p3;CG)eQgw%`&6lAJbC>!fjDl^ zO+&|sxary?90@v5AzcpQdIJ;~E%s2jketJvtJC;7X3{nGKa&^t+R2oMCHQf#E#Iw0 zk?4_Fk8yY@Ll*DoYH0j@=DOH*b1N;0@)NPP3?(M5EOGA6)Vx~Eq_!HPd}8tAt(j>w zlPc$Qq!fRD@I-Dkc`8KM0V_ph$cr?yC_h0f0l-yc9j_1iQn|BV)lv&r;Q^hnB<{Vgcyjbz;MXp5s`PRk1}`rhq)xmA4x^(;U4h*gZx0m4KRY_8P8`LvcZ8LRk4Wvfc{oKh$dq4iZAhgp6&@b zh33gox#rAxKX?1(I6qIH4Za=;S$F`K`8|{PJ>l-r21A0oZ>}M+PHQye__#;u4T&E` z-5ELFU2iZXxocw#xg~jqL=D}T=q`tu^Tz7+WAn_DCW%kx$)ojm8dCDi=E-9XhOv{) zxw+z#IWNUo^6FOqz!`=4>51*9K857^X z-+uJ)1Mm~dOsmH14-#2?*en*4-LVn|FS!N*05USB(jvHw2o?jP+f|9O)}%Eho04%z zi&nkz%15?VIY@?DVq3fIV8^nQEkSUg|ZC#L4>1&NE`^(0g|Cq z5d@bZL@Uc7EGmN>BS`Po|aN{sSl{qg&Xch8e& z$rUw^jH}sL@G!}c_aL3KFfLk`yyDFHi;THo&T0?^_6cHGv}n};)d|Cqs9d`8_q=_v za%b#*xfa6!7Az?yb-8kZoeywOrj-A~68w@3LAYo-ejInmwK#Du8QJYsQrJP+8#iFI zxc8P313dreR-&I#u6^kx&4#Z@#?dpZRyS$DL6!X(D78#s!UJdssj=~xA;>@vD*v#a zeo47-{7Mx;MC}10{w$U^ICU`k=g$ft{fvG}@=Jj8@eKR7_T1&4w%Uj`QBLzR?qI-hhcDhZ7q768+c{$5jtqW-U;^(1KQq;Voe1wYk0O zzz9<~2}qYl8E(+*Y2XL#_Vm(TljMz@_@X8a$1RiKRFj(>RYpHUlEE(#9ky)&Ojxkl z2BX0;rYFWvZje`6KoHhQ)jqlL_+A}{h_ z1#}R9Kt`k9I6maf10IA~N5i7yOrW7*_KSm3H11kp0WuC*!T|;W5TP&{S$SNw4}d6V zFYk3T9N)`Oz$sBlW|*IPQ0q%51o9NL4Qf-rYVnY@BqTiI5A+jIuxTQoa5T&qA*lF( z3hWfmavDucLdqe~snO43kym!S1WMhDQNL28{LS9>1@J9TDgB`A>p?05jXgaMHcS#) zwAnYxJ>BUU*&UQZeWh-)za)7j4eoJ^asg(f51|}WpD<3G@&bBG7%w=e@g+r-aa%Z4 ziK0Pojy4&u7X1`_*mcYq03^mawu#*sRRlv4A7%!_+2fbqhL(;lzeJQybCS*cbUyPB z_OtV{1D>FC^c&NODBaEj1hx-54MK$9c?^XA|b4y`NYwhh(@Rx#bnz+l)OkWfto+F zzM;f0eg;am_lod3p@)(sD1Pw=R1Qkk(0F1YQ|rXvfMM`{v`8WT8lu_&)|kvn3kcTw zBAS$I-6AjV!vtns!@TYO8q$lTEOXjASvCN$&0>H|{*5B=0@0ynUq@7$%MZ4S;b(Pie$KPhUb-KgO-#l&6^kQi8^d zturL%FpNMcn;k$Y=#^MEM<`J0X8}GzlO!4lomM|OC5~}!)&k*k>_IoDHb4N>2Kby^ zP}p(F*a6{E<4)(pVe&AV)D~+nOb!zcMmlSRE|CU( zsfV;Df@vB;OXNxvX^Q{2ypu3)ja2@oq>^1=Z-?pWB=IC)a+&n=22UVd0|#8n_bu4n zNGG==2_#A4Rc5_o^zH|O$H^Te-!G7APg<&ao*nac9fK&o%-*WEH*Fo_tx}~#R)lA! zjSHFQ0*HgQIUo>}NDWJmdb~=y5!;T&vCUOUeC9BbLP>BXGn7LvgD|-A!TFrsI^dYM z+Y?uhxkC)PJFz#01-l}QxkVJNm@e;RB+ld?(_fjrW~aTOeehBj9!;c&nFbA{A5*d& zoH~eTnj{}HY}8dG_(0cAa?e*P?4x$7koX6a16TKp=}*MAz1->adQQHi!U71wsgtar z9&#Y;UA!VV$4i2?*gW=?ZtAfqFgoBGUL1g{W|F`vgNv;|(@g4xVy2KVP?)m8nV22f zAapxRVfZFu=Qwv*eZJDvgZ+Fl&+WP$Bn!F*Q4g(_AqHI4&RblRJcnY*^?D2l4s`=A z*^(mDu3>00f=lEYHpuF@1sWeHpBTRCp|24(LiV1TxR2e+#THKN_}gjM{DQoZNQ1;R z^aZZ@aornVNP?>&x|ulv5QCX405=Mv;l$P`Hllq+42eKPhEri3urF-QvNx-uvBpX% z@?ruGxJscm1USY$gmo~6q&G)9)S=_;pg~BsyTHh}Bxe(AuZt9+b7nuqE`URzQM={m zBEH$whVx@sLO+%23G0)#R%Nn$(iSeiC&N)fIX1i0ttx-$1pOq0oPqy>-LQO)>eefj zXSO|{Tu`=JlVn@5ZI6QG5v3ceSU98_C~sVjGYg#P(Ei%djEaf zQF)9@+g_r#E<#$k;7CsK7GyUpH>tKcs+U`A+fr;_rtsYqRC`2}>}A8UovQmdd{j$S zt-z!f5z1J9_J!-zjkG?vsTda}T?ur}IUxoUR6}D(&Qx0c#tVn{*t}*@)9;04zm} zAma%(5i7~^gzZO?s_wA07TR7pt&FzB)W%qX4-Tp|A#s;&(WOgP4`rpi>WW(SJaAyK z#e$Lep!=k{PIUsfVi~SI*}0Pry+H^TF&-9;CmjxE>BrtEUIoh{A#`S5fL7kO=ja~Q zTNSpo@)4tL+?Shei)n9x>O_<6_#RtVGo&f#G#+D9#QcZ;6u);+)`GYW_R}IL3)P__ z4RR40Ov$orLNpJ8pXVzvdRwbsRAFm@9JA^cImlL0c-(fXzN66`rrP{)!N!_#kJQ+9 z*dCp?yZFB2^&~+9(ME^!_)|M>)Q+2*JuV0150>G}wIOEpi}0IAtroM>a?OQq{V_wrInKHV%SC*-O>{=JH8(Qx8_bh3u-R zi-{`6ABk<~lmLqdd&4+mdj$GV)QAM67NFI((R%-EoGm&&T0U~&9xTMi+oWbWVUL{D zg#P+~)^@n1f^QDPnjK|Un<^5t~t&05Y4PJ22SoON(m4VDc7V`&OaW+*)~|$7Kyc=c+}ID z@@iX3Jca5{Q4>$Vt~`IcYnp=9oz{EEOPx481Zi=x_JiYjkQSFM>1a((VeNDarK7}F zh))eqG>ws)LW<@$CPyVFj*g5PGd4MV;=*x$0lJ*(bwx4hDajFrw2buVdkgnPxoe_Q zHjj)9^ckJ3E7S}(uWl&uO)kxd^U(RkrAPb4CZ!sUV}{52ha@E>x~1N?+e7xaQfx7qgjthf%jIt&<2%Gy+)lT*JbE2(jQ zQJ8IOM_jQSaNp9`c2-vIL}(A$&`YEh;HXnUXZVJH9;716^;F=S+5UH{^r5_28KPt$ zgZCczZuF`YRi3I6`E&oSPEfCR^LCr+_M|3JvqmM5dp%z8xais7`8&_EUNK%(Ucc~q(OczR^iK7ObokQn)!`=ws0Zi7#fihkr6Q^ z;^~MNBmNTccEraKUqwoho{_7Y z&3L!*e&d&nKQ;c`_)o`QN_9*1OC6B9F!hhAuS^&;A#pcq(tizil2tj*A6JeF}V9<<9?CqD*_HX%%=a_@ zk!j6x&kD?n%DOGli}y0bmAgR`Tv7|+TW+xA1mFJe9xjF3Sn47C^ ze)8r^6^RuORXjgip1orB3{cH7S>(AGJ z)8N+N*AUSV+c2VGLc`>S=?#?)iyLlf_(j9!hKC#WG_*H7({Q}u^@fic>l%O7xNZ6J z<&Q1@plL|cjHV+^U$2N*F?2=Fis}`sS8Q9+zT)%>TeG1#wRu_dea**ial0k`mY?79 z$6GG9jBROXd93B^t&z7*xpmF0f4KGX${SbyeC3n3X>J>RTf=QfZu@GLan<})|Fi1i z?MrUobo-v$-){|So!#jti3X z{8yQ;()()({cf{;U~6q7UM?oXto?iZ0y@PqF6y01tZp~HS6UBF4;$GeXqhVlZgS0c0a*Lcz2Mbwzmea`-^ z3#Wb}>o#785MdOC_>Ned6qt;Ajz}G->2!CKyaqWNx)A3KeGgBzvgd12j_KacuD9Zs z?cICv{Q@%WdLatE5WjXvA0eaq3A9~lZ~GI#eZ=YSY1GO7&@%gDwb`$AU*Lbz6{#8U zZSOuMJ%tv!q#T?XbwtjULx7L+?w0QJ-A#zmO+Y_dyZ7Oo%zP=NI}g7d!!zyOTcwTi zPT(m8*ES+UYLZmly%N|Ys6GLttDtXffdr0|cS;|0e~xd3=>7fZ$)|D@YS=DqmdD7q zcIV?>!XmwZKRdji0MkBrg|WXB*|Ho!ArE(GM2z>9c&4Xil7y1HNM-U%Gj81#N!#Iw#XKE@6R1*GWzD)i=DH^z^InPyrKKgpq}mGJHLu&KfzP>aR5X_yyS@>9ezK9as$Tt47;}!?I&Olzw`4n#srrQ zxH?A90GmXIW7zX_Wix)ICq7^}OX;q4z~+Koq8NrNg1c2<7?CPoju(x`=cxVj?$we% zUOsr{C}K~ZA{&%euc+iN&V^87^+&SckNxAHyUDXkcRH@~U!6-(h6Xr44W9BIB6#9q zX?=?J5JyS85r;;=_<`^6gYFo&6day)(D_;1p~9-|Q}9qoZd=_F@kk7RSM88m*rEl0+wKN~&G3vLy*$@lVEj z9sU73U%Pk1v!TWA)_#&ZN)M?YN>7wl{OW@WGE z43Kmv10_F{LBQ@6%y<9p%kW4ApbSO-FN13Yq3kaOqYMY%x-1Pqc?nb;iZT)-e@Tjx z`lHlK;V2C#zro&$0Vs`91j>O@B+3{m3gsY4kJ5zlYeWqhP{vBpC~uIAD9zG9ltWPd z6Z|d)`0hzv(L4&|5FOOS{%RT_bE0%X*e$c8-x**+%@*m*z6Hrc((oh!QWQu>l zzL0IiA4@DsSf2G(h`*Gq@^g=qkLc5AT2|Ar&Nz}qtt-%E~ydaCY0|< zcT3ArZkC!*-XpC*xdn3jJ?UQQ7L@m)d{??3vhQ7->~Sl~2c?xLAChiExfSI(>6g+f zl-s1+Q9dlSqTDY14CSw+)hKtM{G0TM^mCLur8OuYm41P8m$Vk;uTh?rc1w4ld`w!0 za*wngN8&PscwJ5l}?QMRJ|gY-+3PfOcSJ|jJh z@>yv+%EKt%l>R9F3gr=L2g>K9M^GM>cA|V9>S^tn_=7e?$4YbPoFA z>(aYYC(8Gvr%=8x{Q>0%C|{F4l%7WUk@O78kELf({#`nZ@)PNgD9@w(i}a~<1m$Pa zb0|NTj-vdB^gPN7C{IdXNXJlqDZPO5E9pg)7p0d_{u6rbNdyud@BXv&jr1p!m!y|b zUY1@#c}40%X+?PgG0d-`?3PZTgm8h!p~1uLr++_T;QPdYBzbCn`u7tCzE2EDl9%?U ze?MX1`^11Gd4HeH{nXY^7|=4iojj$8&4Hz@B2NO|)v8zUZh_6Cfh`NdgvETD)GZH_ z6XdD#G`UG$fvrCG$lXe~a!fg+`h{w}>Q2=osy(XTsGd$AoBLe3;FBjNgc9&VZC7ehxK#oXSV09pI$j_ zJ#X!_c36LJJ!HM(-OzXa-<8h2bM8;)o;&x{xjpB`(i8OWoz?H$i`VjZe9wlzJ^$^d zx9)vw$6Lq%_13z#mc3Q?)~vVU-WvM$u(t=i?e|vaTL<1+^j7GZv1i7d8Ga`I%#bs& zX9k^#Ium-v?~KQr|9o@%U(df$7+viV1s~EM7^O(C03}#{ef*P4Wu&9|SN_SP-WK;KMafRLs->&V2gbW8*B&c;A5~i_raqv8y0*eY|J^ZJYRrK`4Vi*Kfyw)f*tt^ z?8@K4uW=ss{a;{Fo`iSfQ`l;2;n|o6zs5p%H5S1_uZ90&F}xQ`;jdT*Z$%?4x#jRq zG{HB~0?)(~u-{k0D{&kA5dVN>cL-MK+ps(T3VZV{*rI2E{{zY}*x8T6F1EnxK7x7s zEIbkqD8r>!mBlnbLngQND=0Z2%XiHXp<}A+LR$} z@(qT#Hq{V)R+}m&tFXj4L%&|Xep1zXeU^Uy?5Z|(41329bJx#^*SAU2N@`FRl^EJ4 z%m{aUoI7L2=(sjDRf8vR_xc&AV1c~?yt7O3*0?seA(QoOszC)MQ%l-z&j@dukTE0N zV9;l_JzG%H_H0JDVaANOHjM*Dz`C*~ltZf>(x$l~uFaj-Fs-C*LU@}rWBq!nZ(51b z(02R!_2KK$1M&S?*YAg9srQ8m&KH1z%tP|+1*i&dMngDVz*@2a5YEVmYwI^;^0bmn zfM}Qz2jXgjd(eViXJ*;bPmA)&e(B?fMu$l5<|jR-jUUTDdCn^u8_%wJ<%wUtlM@Do z`_X+qH2K*6*-h#%53qmMNJHh3_Rm_WOs=wj?uUDxwSV@M zhRJ`mfA(_v>^~uKAcpdU$Z>z<;ZUuX8r8FT|&5_?6GMdN%9y zfM*!~QsB>}PYHMp#eHd*nTzn=Q(HazHWzbrF0L*|S%o{(Fmq}4HnUnA@v9zw)MdDq zj@KgGqlX8!f$jUQ0W=AChT2PnulFYSW2eIdJEo6XcpY($TAb^+cAI*~{jRMRGnnhq zcY;;#1jW^L{ktvKu)fbm?G5PvZ1j9Ca3b(t2i|wRaTt1?F`}_-z}Sw4pFJL2k^Y9F z?s|+_J$`9KThz0mz;7L{Uyl;RXb-tAG(sS3%_F# z-shm!#jIVT(?)!zQCo)31Qp#o4b+>5_bI?X-RoR)o=XkXpT6jfDBk<0F>%4%k6}#o zPSoN`3xYyuHZ8#z6Q|Qd%b5#)RtGwwk)F+HMcheQEr_4eb*!-9nMEioQ4$UHP&sjj zEKu=Oc4q@-w7%Cos|QUI&s_ovtOw=L{8@zeI$WKH**^u(=P_FMrig@9-iz=l5A$UryE+Z!wPu7KaFgU2$)24HOfAFgBVO6eu9JjF#-|^me{l@E1>i`T z7^xc}QSw+%h_}#a&cj&n@z&!Ladp8fzMV%9U*#i6QknWoH8wEHAx=)bvXRkFgPn3{ z+)0WO4H0kBvy!AQ(c%)u7de;Yun^6X3%(66&!U5eyM}@Jr~qQbVd{{ zplC#`OW3o-dqv-Ro+bL6Yp<6})48abeka|XcrS-!HtH5qkT`A6oT1sa7NJMT69 z%4HYze=)8TE=VFc&ovGqhkZGI=lGyGRn1_cK8krsxZ(Voe&h0^iNQ!(5VcS9lBC5P zls&j1_(_Tp9ny@~1BUtdPJJdWAnK=)5|Hp;c%4hwy$z_J{()Y)-LzxGHi zYKc<@wLv3DoQ9wgBjC!T2o^CW{4U}?9AeVK2qwZ^kA5JokTfQJp&BpOKJh;CF(DpQ ziLoF$>25uKrQQ-0LKm!L&+|J;gP|IUItdq?Bhzy| zJ)=GlX6N8OuIGprN$2FVfnX)BOEkkNU5qrRAEHAVZ;l=670G$x%^XLhr_pa!_!HNz zhYL;v@#qt23O#*u=JOmn`ps4M5H!Q7moQ7XAlNx=QJ<)7S6yf^?jQ|_Mp|$Ufm<=! z)W7e7k-qWFTPJw92IPuSj>q}vO%tFX94}+k#W5=&;rJ!!3AaQeoOYb`D#9H>*)t0W z1I=u<3eMq5>CW&{-AgfkH4I0D5zhNK_i?5hs+DF0JwfxC&rWAZ2=9Gqf`oxOJVm$` zJ(~^qmY`PzIj>jrgYKOJh1PSC6Ih{MGlV)#jicl33K% zVAMjA?z`cg&nuDyoPJ315ypj8!STs?Pd(}+&Ml~ekEGz-&K!gD@H*VT3{P*N zh4=w|C)#(_zMSWT>nL+xL)?##O7wTKU#>L{f|i_Tc|DsEa{!q$pQbSpJtydhw-BCb zM7egtbyim|0LcJ?m}UZ>F??JE>{Cz!`3wkaBzt;mZP$X^xlNK-T$>O!Hm9a6Py?T% z(-?g@+hJVdldeJ1lJ27Z@$n@IMR50)L;nu_iGJRQmRu!YG1{dW!C}5uKgIa;#Thwu z(l3JllFmb|h#6nUI3?Xd7$O-(zYygK`Oyd(;Ia{lV9nf8roK0;y?zl{p`T%o#Y-VshX#gP2x{Fi2*&;W@h`B({EvIiraQm;oD zg2xgEo(q|yN4Xeshxj8=i=bmMvS|!&B%hXn6ZXyRoJWyHd>*Tt%Q32*_l9&FevM1& zdH6=Mms;f4X&w;j*#w4xq8 zo6`=b1hP!$GKv=C#$~)~uj^1N@gbrEK?|h23yvk^&$suRD3Q0sbszdJ^xS5+UB?xr4fa3O8a;03&QO6EDX}hY0f&&I!-BKX3|IsTHrneZc%aCb=51l zcFet~0&4zrUwZXW)Lg{06w)^d0_rD83HnC#AZ$FY=eg3S(E12YqO&->bNiE?CJwa% zB}pP@-EKbS4N=Kr+)o%M+1%roBW}k%6kIFjw8ts42M472lhmQI2KQ52e0&L$bobmo zYbU-#&{B)^&S4{ZtYY&;Sc{@P(E|=eC97F@#Cm!~c&0Jx%l59rF9aLc7)3j-c;~km4&NaxU`nK8m@5~e4S`Y0N0w(H>SZQ+hyNmSzXX}D= zAd+XKDXhR)aT}6&K#z2$kt2%Za;a|#=qmGs4kM_6X2CN2QCnPZ?a}lmG8%E6AO^|hWmoHf?#$sz8M<1$!S=$wo2o)8b^ zJ>)Z&Br%OWaYv#6ve{|I(_G=Rjb_?LyQ+ zJ`}jQMOw(8h{v+4xA>amA))Vq68XknjM@QeittVwIFazzG~i* zD-~X=Md$hjbvauJ|KIm}U7IF21!1R^-ImFh(j5M+>#fw$7}HE5X-nLHF~)>*1~E#@ z@hf>-iH3;7^kCkZ-}KgXdh5>g98n6_f{D7geCKeJjWrGSRvw_91dc(yAgFWji6oa; z9h-(vS-3tO_tE+iMJY@}Nvlm$uzEZR)>N>I&5)+{4==$T(gmq7NMAG}KM2>r~ea);@VQi_k7T zMb8oD2+}mX3Q!i}S6W}Z2~W{Cx_T4d3veG_Z={+DDuR}Lr1_|$kiBQ4R)T=9Q*vC9 zg5go`Rm7m6mZ*1hCt;-szo(&AdY)RH%&v0C2q!ek1WQjXBKqw@Yh2jSHq#lT~lxmwMuoT>I}3(@KV2f#@rcR!u-`^ z&=(B|nxeXBbO;}L3=7kMzf6n>)x@vR^F%#F-xKW^g2rzuMw8>6 z*Vnrj)GzG4#63M&=XfRviB70R;RERzV``IN7Vr?w5-o|D(1R74WdsSeL}N|gs0Eq{ zL`hSCQ~FKJLf(d$={K=wMXmIlD}Kdn5%-DbuE!xbjzrs3cTeAmItZ5>LK1) z_!eox#MOwKap}vYEUkuIF7QY;wzkdy|AG?!W0*B)GndXOZqBPE20e zAIiH(F#M4IMQZ1(7mehmIbf`jw6tGN;9eBWl}{d*oqYWK(EbtGu6MR%khbGrL}{Eh^Ruz>lR zKs_K39*N%Cc<;F8|D5MhP{xnJ_b6a-#={TceIz)3Z2m`r>Br-PBxrt&UPyxL+dM@- zj31I9``0~@^wf{Z8%gl=mYF0mX+8Y=@kbH_-zKX_ijyDjzv+=AIKGY2geOzzGlIK2 z>(k;ME`|R+pCrN3mrtMjX6V-+gJ+W9=wS4EIrKjc zexJ!i`4q9Ym?1wCjp4hwVjjW5l z$hPQ*EQ);x*^D>7>513;mDpCfvky9z>)#JKO?du#vq!`gjnWS>=ZV``x}Rt zhmn(`-qWn%sodf$3PGnSou%Gm$-T7P1E3%=Xq-LWiTs(Q3#jk}M0bqPz%llQ=u= zm#0;1iWyxF58(=|R^I|l+={*Ov{U|esg>>V|2g)-{{nmN@4z1a_1M{bCo=Edg&p~K zV~_tm$gp=W_W0kAjCv0ulipTj0oaDjdE2q$euwl3@&P=G?0CP%?&Zg@`~Nq{8}M6M z!Jhv8$asf6_iV2{?Ye&g8SM@slU)Zg*gYkykhSg^?7u&Z-S|h4o$e@d0~|wkfEVrA z0bXXA0bWIh0LlsQ7i5-u9lQ7`lN{~ne-r!p-@^X>ccin()rpi8$b|Qy?1p{+UtstD zSIFje5qStcM;?On*h&2fa_nkw_O!d)Pxg>Kk+sbxd&%CikL-(V9g?h*{bYZn7IU)*(%NZI=t_D zuUj*t+8?h^!|9?1+^skRXNr2eXW?v|gZtuKaq_`=I3E|_Lfj7*;o@DliIcM9x*s+i zxS}?15hYQnp@K_9Ro_c-nW$xVGcFfZdapngvzWtP)UXfp*pCHKWB4EzaR^IT#)_yM zvMOqatYZULidu8Gi`ol6$0iQrh^Q5D6|NR1&|ZUUaUDEtp^gT8Y@>-50vtm-KGF^$ zBJ|?g47eVB48-x2BOI62NOo`%55YslNrn#-l}R3sN8pio6dsMo;IVid9*-Mw6P|!4 z;z@Wio`R?1X`&Ly)A0;E6VJl4@fBZ{VBw7QT(|;Jf%9zKsu6E}#p=xdIo_#dLo%$s(H^a(CTAGgK6(>6WQNm(T;~Qo4*T zrw7s%RHa#(6Xy`rXdlhfep;XdbdVP55G~O%tsYxvg=oq!BLtP3fq8?qJ9fg5uR?T#6)Q|G@sN40tdfg9)$-v%h zwd>XUQJD3VHL6>E|F~~>(qyYqGYb87)>Ae=R}cF2QFpuJ-;l45ac-^=4Ha>&&d}If z_XM@kP-d^t@rK!2BIqZAH6`dvldG|J&S#jdC1Lo=8a2h}OOu};2a<1&ac-Uoa!rPX z`H591G$$JQ{bS#JYmC$Tx4eG3B`(IoFz7UVBakM$kYo}hnJlPG0=0AtNz8$=v~Yle zWBCJP-u$sKE*zW)q|m-YquKZUu;Yb|pl&RB_0iBbI?@ytC+Br08pfhpppG=@MUj2F zBQD00@(-2&(u7MPoM;$J%0E;K9D2QIFziRYmY=GH&6FQDv&%{HQIhmgE20wPY@J_6Afcs z(F~-%^1#$PQ@#St1 zKTcF*V`6e{x$8I8q*DSh{SFElsgR!ahhAph6AvjFj#F{7iC+4s7)lAf%z8_pbS#)T z==FM@xFxz<4UZ0uXlX=;1M$U_5ztDIT5Uy{wV>JcQtRF*n?#dZX$7e{@mU!Jg_uW2 zvL~6K+c|HXwm*@{ZV`aYZz|41qEkm|T)WE7jPy@$iRqbUoK&XacZOaz;Yyzr1F3j$ zC{v2bGHsbsN2a9KV0Mne5#11EBb8UG--?VuoRpQ3CN&hfCHkqJ$g3_sBAiSVFL)uQ z)nV=Vzc334r`oM#P&RVXS;8rAI|wY(v>lCREJik?@umKi_9(JPkv&QrRf%IMaV#Z{rNqo7 zW-c*viJ42xTxRAn=UZltGHaAsqs$s*)+n<^nKjC+QDKb=YgAaH!WtFUsIW$bH7cx8 z8nb8(afc+|T}zyRsO}ld)EbtlH7rwWSf9ozTcQq9*Q0BUed0SdvVOmNO$Gw;fYPZabFD$acg%8})`k6t?}HJ$~OC z_`^s%HTyxcC4b4}Vca_t<)lt$C!4Lv{6JM~7iE(CW61$6=s{TrFW~9@!By zi{%x6u>?=n%&v~d)$zERS*8_x&2WOuBYO&4;#XJZu zue;Cv~@h03#*Q4G}dP}$&e%KHlMi36fuz00yO^G*Q$B%k`7;mKMR4eiy PH|o@)X;BICzq{yv!e16k literal 0 HcmV?d00001 diff --git a/test_proj/addons/gut/fonts/AnonymousPro-Italic.ttf b/test_proj/addons/gut/fonts/AnonymousPro-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f6870b7c4e5af796982cc026e159e844c5ad8233 GIT binary patch literal 98424 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≺`{ReJ=xB61ni;WZBNY2Q_gY=S5D_ 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 S6E|)CSBUoGWXG@WbN>SdkO*x6 literal 0 HcmV?d00001 diff --git a/test_proj/addons/gut/fonts/AnonymousPro-Regular.ttf b/test_proj/addons/gut/fonts/AnonymousPro-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..57aa893826db03a74c98480093e5c9942798391b GIT binary patch literal 112072 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 AjsO4v literal 0 HcmV?d00001 diff --git a/test_proj/addons/gut/fonts/CourierPrime-Bold.ttf b/test_proj/addons/gut/fonts/CourierPrime-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..91d6de4f34673fd3030df55c98016ba84e5c8283 GIT binary patch literal 69768 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 literal 0 HcmV?d00001 diff --git a/test_proj/addons/gut/fonts/CourierPrime-Italic.ttf b/test_proj/addons/gut/fonts/CourierPrime-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f8a20bd097284f65ea89f88f742f6d2278df9215 GIT binary patch literal 76496 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®noDRk&?P2sT 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:Qx7$}1>ftYCV<)RxIqM1iv7(R2CE)kOJ{gmEQ;xdvqF zAc=v4kkgd3MJ$KR{n4UwUQWf37WUCH=nv4bZ=qO{R%H+$W?X)C>%KA(eHvM(%q(Tg0Axaw>PknU0bs z=Cs6FoN7-xAAJH-4(WK8yB;xjN>K8Ql8Fqv5>NP-=ROwPoWDH9`%rhs67K4L#rv zp$A|Wjl$%p46db-9>9yq#>3%)B6I~zem8cOk4?ZG1}4Y#sklzn)_o#ljIKW|+k+q| z))nzv=jy~S;f*Y#e(6!PX|aAR+WbbKF-j_z`FH}28NW-qy$^X=@^^nAp8qI*mnv}k zP~#%@I@up6nq0I83cOqyICmJ&MEu#g!$y?+Nwh)jY`XVty7zLRXp(mnb4I__L^@_= ziVY?C!mzn$7#Xyl_B8q`YuW}nG@Q9!^x`QZRQf4QvQ_XMLdic8hCDPu}9+XL1%6R(8br#az z#V=7C^if3p3Itb0cT(J=xHC3dS42cen;Lq99Ms`Eh2KHk$?2ErH={AUcskwLO}|Os z$~$qSq)6IFC3tJ#5ouM!2ISzp$jShbSVUF^GLP4bfNV3~lJp-d^6_LU2K^lFP(VP4 zd$DMT@L+mb>-q0NRrpev4@zrFYpSd8i|i-^JG?>;Bk0=Z~TdCFL>p|iB~Sr)a$_wXf-Kv zFNAFDMyd_sM+91yjNCH;unp4*yHtWR9m`a^GJdcgoPP!ihz-p%tbZb!a{So5u6>2=x88jF?Kj`rUbwHzjK&*! zy}i8~Mk>6uYIHc>^yycB^3FS-T+`MjBJk8Nf9-2urhh^vfJW{%cs%0JxdPAC=)FU{ zcCOevJ3)d9)ZvlRgzlg{^8ZTv4#2pIYwekP@2;d(@740%T~%u(t6GvR%d6@pTg6Rs z)moB;Y)P)NO*ddu1Q-Gigc1@$h#v&V%3!b`gg_vKkc7O1GziUv6!J(yc+Y-$zpeHE zotb-gSF!{+PxkE0ojzyUnKNh3`HS$s6LW{8RXZr2@Lwt8i;`&WAU@5VDR=>7Ni=sT zt-=d3sU)Ez7Ikz$UM$TWlVd)ZA6ZC=7Inn29d(qPM>3_LnVml;T8A7atqd}y*eh{j zQW&L&URs(4ZDQ(5|);0-fn|hCI2UC`JInlHVu2^kxf#d z*0kz(iD~iz#l3&{%DRyCQ$GZHC}Z2_E9K2}PNmToP=6BRhNP8mqjA&nFOM6Lz0GkW z_>lh3@e6-CCz%h%rX4tG zD<0fA&A_>3y&O2x3uUK~bAdef?_IY{n`Y#EBWeBTi)x=*&bxn=qxBJMlvp4CbOCN5 z*D6{cX_dnI_`kG1UPddfz+xbCQmnY&7c1@v{7E9b1LrvD?-Tw)o3y|vNJiz2jd zCBH@XtQm8fu*8LINK%a)CJl{9#?U>m3EFq8R$N4ehKjq0d=lcDAH+~h^tS2aN?nCo!Eh*4aK+ zd-km-3)f#aZvv-T=oKT>x*Mb!!zT>l!P4FM2t_b` zn5<+Dn_wyc>zEYWPkQ#Um(k6t0-Ycx>lYEBUDL)CbO)!A$j>fD7dvI)Kxb`WF+6}f zWeCl@ppdDOx=TJQ6;bhs&fz4EUBx4dqg$LAxSd$sP+wb9O}96*vg!6F2@GK^N=w9) z3}FE_0kc<$9OM`2V<3#ekA7%;m;^sHblqH2Az^N}BDWOq{|(FIZt@%0ZcuTX}))jI7o9GvXeJ zGrR92?EqeLRkB-z=9RFSxT7S_7`nUco4qD+XhR~OD+w2oLcL#{e5yR)3mBkLrQ5#1Wgkdam8O37 zGCa^KQ_pzvt+9F5n04z~R@tKdLZjfH4Kd!syMq1ee;|lhG4Qu!&y38CPOQ;ptluQG zV~zLSb2Q+k9Ht}qJ@*`?zIp(=4Pne;{|Y)wKM8-rh~%DSoTbrjp}h|4gibmI!U0Ke z1DNtb=o*d`vEham*TWw4d7+?9L9~=JD>>U#wnpgU}0<# zY9VG_kUN67>~R}Anshn_@_b{W-+3omI8G;JS@tA+6djkCmJ;#!;}I!oiE+^%;fjsQ zjYYY8CzOj*XbbKo6m|&D$&|f-!^Cohtb9R%k*xe!5JFH5rGAQXwb-f4&D3VAPe4xn z3|1uMxi}$47<~c4uEO!c1(PvIhG@JqHHTgTODQPEt25CBb0i^%b{^qUkEHSBqc`1j zG?}MmL`OQpl6%|Qdy~Q(k6^hI2!OYGBFcEv$m`Swn?duLOV_${xkEhl45^m`%Tp!6&UQHJyTAfYTD63SB9 zGJ?6pDxiOmsfVb$Apjp1Q#J$q3IyJyVLHjT%7@^KPyoRQ?0Py8Y(r+3Ov^e{rrfb^ z(&q13_i+97H4`@W_Pdr-?V_|=^?AJb$z|8b+R?6QivlaR120;|o{bNS`~dei1*ZeS zB)R<-tcX(HB&vuPjc7nS$#4O;&mnp*g&T|&0>GSOov8hRkQN=?Q&I^1j&UgsrbT}eX zlak|OYv)h=!Z?|)MGHDwEOW9PF&ok|;%yuxR52kKKZNG7y}7x$5SePs0@TSb0#J;6 z?E)X=jdBBBW>e&5I*4xJp$gJGul?Hm3Efx zc94EYCgnUQ2{LZ7P`LuZ3C^wPjfQ*9cjFzBXYAO+V_W zdOxv-ju((j66IH>O@64yqo9yGlu^vUneNY)TPkr^lkBTmP?{-iQ}L_kgx;VJkmFze zl^lPH9M6h}#M)?Zj{IB7Cu)KvoyD+1NdCj1HMZ+;)E_bd7}$}q8HW*A@pg~z?Rp9V zNJ##z!E8)em0+piC*wokPAZ?_RQkdSzN35+7abADUo6N-u|)Xl^N{qo@|x<@P8%iB z7?B8XIOTmkzO!iCr#&8HAH&99l2HOs_j1ABtj3_E#0sM%?KG9-TOdl}&*wqj;c?$t z)yZ>kQo`BrT#3Yh&XpMFurcKm$bC*{PF`^yWn;0ZV5h}7>jmXCEd@1n@;OGx95cjs zR!iYI2&p6=q^T4!IOcdNJHR9%c4hye zq>PU65X1T+Ar&tYzF@kvlQSIel83L6!uWIKe%CPrfOD{SL>)@ldxp4k+!aEXAofFu z1|Y2(xK8iKRgFPf6TDv}?i`xF%uqx})rk=vRi79$1~K(7kYnN(%<=Ox!&yM`N|dnT zEIaTx`Y|*P`ym%_ND!KwKxF|(Ff2_tnh-U=39drV`56MJQ1Uq86Y#x?_o&O&Rl)Ok znmrahD{n-FI8o))(9q?V!>Fr@%6-2mFDfeMMdd}&FO{ub=vrc&aCq)7a%r__P${vZtP4hlmNrS$pSpUIj6NV&tVa1*`(Mi}%)q%y?=yNFG>BSg zry?P3xxmXH29+JK(i}Z#3k?JNyv3q+NtcS(NhE8p9dsMIqR0!wvKva6=!8_bHM4}S zp@*{~eyE5ad2!iCUu5>QgkMDM<1R9Ll3RHZ+0#aGF~CW>xa?CdGJ6t%^o48x#Vbym znDOUM&B_8MH8%@)p0f)fw#&&J(C(2PZY4RxLTwdJ=Bk0M=L*6^4sab1?V-343l*(; zCfk!ASFmeO+m%cu)!|52;xCsnNVD71F|}}# z)Go(fP(gSOPE``ONr38HOuS8m_#_Ivx`LHw&7D=ABk^hoB(s|I7J=R3@BvXPbR3~& zT0HM`n#>?{Pl20kRr&jM*Y=N5Wl4{H>>?cqr}U9lkS9 z968%Y=yuku@`%;{xcAU2+qS)O=-z*^|z zPF|SkKJ<+udk*!dF2mk}HcG8(IR)b0WwwSbtu$k+&-npxG$NHhWOX6?p*G~5R;{F}JPq&X=zy6^uBgcETZ65Bt z|A}NJH8VNJ9+f+OxOV5#jXU}F-F?@fjdUYig*L{qBnJ6p)GY~uC`9Z&i9{4iOh904 zr4~YB9g;~hnZb=CBq@h*J6FDhx6^%=8IE&rI%Zh;KtyT2;(JS3;(J#+In#0W)97U4 zi%W>pq8u}yJvXZE#prkm_)M7g5Kid1?kw7C*!5r)#jXDuSSr1HRE7GCRF+&8UtzJP z5)SM?Wd+V#K-lc+$1}tUYy6MGf=sDbiB;k3Z)rTLZ=cH|TgxLo1MFe6HwNX}S&_RS zP3!|G5F=8>;~3C@#O4HEC-gBfP2td2o|9!1PYoB6*|_;^P@FJ0ZGlvc_b>p*f5jJt zhx@*%tf1jIp6Hkv!w>mxj(}R%m$ZjyeEGIT#l%MWTFE^#TTkSFy-9*j2aad7NIes~JtECw*P7=k!j34JA)o3!gHygjMhnX8E*D)wE0 z1E~R$cyc@!3yfBgZuK3CEG;M~i+b@8weyAK^pNAcGc+wBDowjDEj}{t?Ay*v=ze{J zS{WVdTcrLdB`FkmYzEKPyf|8%ge}MEUzpN^C=@YBgdg5leCM>kv*Y;fB+0fA0wsdJ*`;w(?G8^4+2mE zh-Aje96HrB1>_k`A-Wr+tvOS2oGBqzI`M%ngI++BafpA4U0o0FTs+ys^4FQ^p(%0E33Iiw+par%{E8F3!5-RDc!0)y7yAQr z+8jmS5Z3o92R{YQxm7F{^8dZB;y5gL1gi|{{iBe<@6Z;3dVj!eo5d|w(&a$Z`*7@! z1$ZKAvF<=)8Q%#k2V76++DpI+kJ+ZKAzNjeF!NQ`N|$+%NDI+#nh_B?KbPk;0<@+aLhEgj_Kdo`B)IB>ql-4;D?Ns?8 z3PB24SfFJiLeLW{@mQ~HWS zhAe;V!G1FFl^eLxCwsq<$6)3AKcVSPq0if)Z9)i$d>taHU7A zqPW;bO+8p4n0&)SL>8BsA`lM=6$|)iEtc#H(-5wHaitJGeX*s`mE6UYf>$m^!$BAb z4S~Y6001U7Tj0FV<|-s2R+Nd0xU$%+a=N3R^sFeb@tfNcZM|S44=k05LVZzenRYD( zlW!hPd^?&`T zvbf^#=xR&J&(`8viKsar0>L#RAvoDNGz52y+rQZCaxl%8FkzC!nAu5z4fEf*&*c{nwap*NjPsG(} zS(XDT$%O#F;N__-7TecE>SZvYT#zq;0AsU5m+hl2J3zg(@3Q{aJ|@S<6MC-e{-v3z zyZ!y8{yvX-2Jh}Bc3bX@h^)-Br_o=Rfm?!jcb2fnj_ceKVxG)vNgFNJWANQ-DpXkP zVi0Z=N_^`etA$*}pg1AKvV~qPMQ)1X^qLXZc8ZEAXox;qFd+j~=)ky`9OP@dC2Ubc zUG4lD(lbq*e%UO6l$r3h* zBJ){;S7YHN@ zs)auTKP%_w=0N;+%a+QgkWOe2M(@QX3cgKRig5P+yxziI?q@+UA3B)l3dxTmZ@U)R zm%p$gWSVaCtot!_+>fc-_qvPpUQZ}lux{#pdqO{Y1HY6ykGrVr ze7_{O@5rvLa|&%mzy*br3M=uD$(Qs~@sP>)W)z1)r{*)0aptjHDbX8u42T=dV*ZB{lfG z{%CMufH&hTK)h{@ti#&V~y@Qo4HC$-XFS^qPGFz zo1keZJQ!9MP%4n58sxGf918#@tfVv@r(8*=eV{hY(kYZCzb+Qg@jIyfA4{L9*Gu1Q zYOe1U`X$5Xir=3Ht&xC*zFO7peOIXa0qfC5$-N0;VF) zRB!r?I36?3R9s{il+}RO|C8U|NNzMLOK_v1tP3Sw$T;+xK*YgY;0xnO2^_?Gx(ICs z?Eug-lU zMy&a~mY9BCIbqRJUKLOMeg3!p<_pjLTL#guQVuSSwGKJf@XMLtzXpb~fRR-EaImR{+YVsq z1@!O)1(K~4tTT{X0iv!2_=nAA;vZl&g?3`|iaA@HQRI$~0T{SQHrHBn12RWxkZx5X z-~?^)kAC$QYUeE%Zs<^ag8<(rlQ#A-?+~pk2gTwNw1*3iZvQ?md9Fzgu;jTe{t%;G z&UOpE*IP?aVY^AnwBbwgZGUM`ZBbiW->d`N9-W;CF8uLv|zgjDHyZ|q18kvI)#>3#&qyRuo<}AcZNuPi{|K> zQm>aiUirtIp%nZL&CJTueK)4g2o2+x$7iMaUdNfnGDlDG)syPQ6BOq}Zf z(P!feFhbtuBkCu7JopZO8XdyeVgmSuU3~+cHNKU)Ou35-Ww+Er7>Oo#j{=SsK#<+T zo(6B=G3Mm%^YB5N-Ru()@>PVCiI6%Yq>e&Rt2XQiT^P5kS+CpEfzt`dGvJScC>$sO zRG_LHTO@>eXgF&_Efnr1c%&yf3gTEWh{4(Ccav~@tP%O4v~R9vcz)CZarktJp6hgE zWo0?Cvq@G~VE2@FFd^nR#^pq^=@t}KNs}~Z0x}WHaT!s7Og9nWK%1(yligeJ{x?^S7CXQV z3%pgR5o*}kaHSBvl3xia4gmN51JzYgxtY*NR9FA=XUQk4U|ts4&%ExQTMebF&?SLY zd^IqQ1?(nyT}w|Eu-S-bn}ljeI9fGJKOF=kHbn|$T7JPEP7-NAhz!!;9Eo?EUn~(G za02KH*!=3sIpxJg0$Cw+g(TVq08RqNBnP@ZuxJ>Z$mRu@f4%YVdRnzqDgF@`ytuxj z{Dgq%qCb;0?xWv>kJ0aAg2t)dC{B8Uto5?*K?2b2^mBxM{0?OwIWa=de}M7#7r<;0 zqyjBxqsJl2P@^+7U5Vz$M%h9&EhZY2UC{3bBEv&)*=+@`CLV8ug=H@TJ%r~I*OrTg zLBDaBcHlG97||iCgR$kymg2O`7GjhV;3`UsK_bFBjDK+waD?(p7H#MN!Jy0a-(8pv z1E9`$uxry;#NCoHIL#2XjK@3iUnBmv>z7;d^EwhvKAX5AH(y(NHGyp?uY@e#NxygG zyqggBT!Z7rIS8WQ%;cVZGUQ=rPs85>Wlkf||QVSA7r=MN~4 zs7tlSaOnp=5F4$+l4eZ|cMiThZwkf?n%K$e(lDPlA|^g2(R#)fAC+394a|%P_gxnm z8Iu}e<9ElzWT3QPE01XL$lb}W!o32mc~_Ct62`~F#y|CG=ZC?$Qz$q?9gtjxb`rgu z3yxgw5e=uC5)#j)nta2w#E=lvQY=2`_wj-t8C&C@lFTeVlVoQ3-pQF*4tPC3uME2M zyqNsKOZcEAL%q}bWr3~Bw;H57+0Z`7GcEJgC#+k8&dUB!^!EQ#H!5MkD`QwWX;lW~ zdaI^-UF3*O2Y#ALjS-~xOQH|^6hKJv4{!dfl*L@})BeV%KXsXlByi2C3iy}XkS9?8Hjx?Vw;k~f@4=$HmG-7J!m;F1esUh)flwUZ%}?fp`0Fe(8(e= zZ8e})AfN`^;DXTkmKj>Bs3k4YB5fdN?yz*Lk6J!Kxx;2?5X4PhaE0S>KH9LuQmL*F zaiShYX6&H6qAV@y^VWF?EjB~Vyr>WG|8}+m1noXtNfISvExQ%|--=44tY{`%ZvMF+~4Ax6Cx81C)yMD zC%&HKNcu){R`N~B?}FECb;_4g-b__eXQ%#m+OhPE^tUpqGM=4rPo_Q7m06Yf)BLZ^%AfUeL0Z9Y zT^X+L70xRhE(!r7;x$Dd%-%5j*TvT2&Bc!tpD4*G87g_IbaPp6`Q;UsisXv?ig^{w zDmGQ@oa31D$2tG3v{hzQ7FD_{J1ToC_g4PC@^n>5Rccj1)%>c~s)wq}tDCDktG8BP zQGK}j%-qN4o|ya6+}G#6Irqc4f1h`2O?%Dyn*N$yHCNW$SaWC112vD#-!y;g{E_(w z=HER3uK5qvvf5i}@2>qu?c=p4YF}EgYC-pcT??*UaNB}=-OY8|>#nK0rS5^cN9vxf zd!_ETb-%AWU4N>fwXw5tYvV}c4UKm+UDs@FPHA>FS2VkuJDN8%Z*RV=`TB)lUU+oj z$wiwM+ZUHDu3h}{l0!@GUb?y^x23wJv8AnLeampmftFiZzS8n&%hSt>mwA_cf7wr$ zy}9fU%l>D%vOHn=%;hD^YnK0{b$M%d>$cW?t=F}Fx%KO<$6CMJ`m@%z+Rk*ucVu@I zchqz&>R8(`*l|_IO&#}je5>O`$4ec*>Ue*}zLmo(53Ianekg?Uj5>lur-})e${F3?CU(-`Ap}>UG}cxuK8U{y4G~vvv%>?U2A`^ z?xA&Wt&dy3V8f3$x;8$r@vTisoBB2#-t_D4Mcof~Kj&%jJnZppZs?iQ8`b-xzHj$^ zx9?|tr?xEL@~bU>>R;DC)PHsVm--*-Ki>bt{x|zSyu^7)(IwTFEFFj*NE^6q;NF3U z2fPE%4!kn(^MSXw7HnO)b^X?Xt$Vlb-IlTK>A~T_uMHj@{Pp1PhmwaD4y_*Q8+u^q z@uA~GFAu#j^uf?ShS_lRaPn}@FeDSu90HqfEh!&bB6yx*k(Fv_meOqFpFT&E>S+!liGL;25FUa*nfk$3uP1B zsf-`PY==QTML6=|75L7A`4r|B7?i~wu&GUeAJ<`uU>?NxTQJwbO)%aBQ;zTdMEPf> zVF~cF!jIrQm7%dP8TJHcf_t!`c2Ii=-jfZrh5RTy*bwZ8Ve()I&QqDC@FzIjG09LL z1RH__^#{RyvH?ukN*IEDu%S9rACXOMr}8J;)OIR=I-6iY;bha?A)D$tolWDG;!kH& zI%)@vWAu%HF}0ImZQA5dW0S@`jXg?3k-T`wTOccy~ z#3lHm-_L)5?{C2zmqx$=YmQiV9PlG^6~2dHFb?=lFt@^O%sJK$H`R&Y@Dj|!Ff^X_ zz+}K!VD5q;Khyja?pZL@MycvCpsv>pT-(Psn?8cV_Boa*6$q5dKm z1Fpg8#JYlZh%x;s?6+a4UiZL!6^7BzGw3N~Q8c zt*^}y;0wzKcqjRKW2A06i2ip&XMRy zb7VRS9E%*?j=t>I&MG*4(c4ZPg@;!V%%1uGJ zuPDC_D)&IFwem}bN*@k zJ_Yj~m~X=zJ%5BT->1G_-%8)w@nhrnojWnU3pHk^dQa)6W}M16m3}JaRP?EqQz7sB z-ar5TZ{GjK`#*i({$9g-Rqs{4*Z*Gedxh`W-?O~?w|D>ZcQ;tugL)mO0(OgG7`qoP zGT(rC0){cK_znK~))aoh;h*8=d)Xl0gLf~(cyq9W?F2PxH@}jNpy&6o{p?co>uTQ1 zJ9$6*ko|$L;al*k=TGd1SO%;F5SykbvAYB`qC3Ds(q=~x?*jow>;gO*VVm6A6?by zRkA%NL&4k8)8pI>u5B;t^0EeJ<1xUip}W4&%Uxc*drP5LaXIZyd!bi#>Ag>?35l$} z!5iP8cX!v1DG3erW7%qhS7}(eU-w2h;b`#mdbPIw#}q|DHeP#QhMjzmMJ4k33>{8q z{jqo+k1(f~wRQG&9ZTXAv(T%#ylR0rv7wX7^CmSk7!lI-Ufugbn^((Odn}JfHZ=7# zd96*IcCVV<)v>Mc@HY$4VhKw4Rh&K z+bA?kW1A5L^>uf5QIzgRQCpkqSXdNmXsVxO_Ya(K*TjJoA#>pcs6qpp-mN!{Iz7~> z0@y5_I>f7|qmpJV(X~!bqbwqFn&jRrq(WkU(}EL4(fD`PKN%Up)FyOMy3^h@3xhb? z1#x~&-d;~*p*O~bCg{31x?u^m3=Z^=H-^4C@D(HaD;62W2%zYIK@TeKjcw@GN4s@z zEWlRijdLws**T{5Hg;usBm12D3%&8KrR|+dS4huvJN)B?e}Zd_#Wk$z9E*!<@N!SR zH@1ML9EMi?STy~_;K$38&=+cUTjv5X&N>rLJ$48KSxh8%`s0(`nr zhi6j2KpxBDS$%yyK_USaLD*PAXo2_Uf^-MQc@i3%SWxIqc8zheQvi6fQ(a>!*=epZ zjqG&Sn1$>N*O-;;8LqJqvNK&{p=8f=jfIh2-~w1=M|iE>0J&2y^70KdZ3?}vpui-5 z;7%D>7!;V}58NdKbr?R%&Q)`|N0+j(R!JKC+!?AK6)GAKBSxAK5u* zAKAHRAK7_mAKCe6AKA0eKC+8kdX1P-vt0mjO1F;Hk9X7H@WA#K(cmq1d1n`RXJcZP zU`{N=(4BH-Iz3fRTH!x42F7Kfx76(Gu_$X34LxtktT78uZ0f}7NWEAV1fJ>Ql)Ll_ zQS%DqCi7{!aJ|4XG-YkcpCw`Y$6sTuvudoICsJGI03K+-lxey#Ydlqj-bzhq#vg!dqNHM$7GMD!wkA*~Gn?c&kVh0cXoi#o9bV)@0^RE5__34riiEMiICWJJ@j zu4ap9V-c+08{SaRH(KP>^_o%SId5V#y-4QgwPH3Hv2|}ZEiUf%&L=h9qNhKpS@WfVf9A&sDZP&+iOK{Pn@@yTD(ARPg+BFuhVP6?uu1UYw)DQ-Q9(SVsaV}s)mig z+3cxGccO%5v=f#n1^G=;2r5ErvlWvVongU%!&Wk>Cdko4+ZI(q#jmlX2Fk)o2qA?QEUbeEcNUy=pPPJF0Co=VSh1Z&m zuf+)RBQdfQFC2N@PNTQ51r&N0n57Bm8{O4Sn9k%Tn_cY2`cXtsZ1yJOg-1FzSG}gI zXsnngVBXeE2<=F3n-E$*A=FG8oZsXW4KDB8g5U^dBD26omv>&lD25-6u~E$Q>FS0K zE%FwlicO-)G>mg3SfbuI6E=ZiZJtJgPL7qHf%aH`o+kJN>_)GaPMjaB{S}j)CtF zjt)44&kBmO5I!p@4mnm)9CECtIOJI4dJ=1416-YOVUvfe%k?DAj^tVkm-Jgl@i_Ud zr+C7(f#M0*Mv5m~o2bktMC+z9$>E_g$+4NrBu5X$Sp-Kf#UV!@#UaNQibIZmQLje0 zE)n%2*MO)OxweXWk!zc%7r6#Sy~s5r>P4<$Q7>|B2aId{9k@e$dEE%uDV=q2?joR5 zG;ck=c4M!Flj7`=PKq-k;!ps^*^9*U{kiTFUn0eR>7*2wN+(6X4EfBLSza!k6yXZ# zqzG3cZmmDRtHhVc?`r9!=m(^eqF;lQwKBhJrIR9DC!G}GAmT3Y=XXeaiTtjYPKthm zbW-#ik#d2|?|U2 za2R~G1E?iGjO|Vy|8|VAkou=sLAuIjk@J~iO%IKOURn87uH;!$E#k{o|6GJS)UCY$ zQbssJBkP}IZt;)4PZF70`O%_3xnvz#HJc zv1g@-i2(Y0rc`lQ6D(@ftP}kFA+L5Tw(Q0yExlIO2tv;1|CNHD6#u{%11ik;uECLJs&RhFfE^aH}ZeSXdhGG~8C`U)^Q6Ls=HT*KmiiCH#5A9nR(` z^g@!#jbJXN)^JC1UD;~5qgc5*3XV#mEL;l8!T=tvUV?jj9jz#a#ElBLR>G$bHpLnd zar*G9gU6cU4~Z~xZ)aQK?h&c$5OWXGZHNDE#LW}A?m;QL!BsjN|N4=ZVvMlOtO%*L zBd6J*Zj8XS2l1)`59Q%>yATGFf;hkuaXk8T;eA^+p zx}DX*H8FJ{=R)xL1*e`EZz3Ef#$1hb)cygqX&6ullshqx>C0;O!=n)O42Fn~I!l<; ziB?hmjc6UUvJWhs3sG_({td&p5cWlKqMoNdl06iF{?vV8_S(c$n^6a9agQj!2d*8c zff<*^(g4DB)+}H`BW(p-TTt#k(Kj^GWQ;+P3$=bcStO_bK*f$PQsA;x$D! z1P|n5Hs|AYVgbfgAzCn7%%xI{?sBXbbMSh%id6#w^T4k?A8XkHEbVoy9`mb_HKCOY z!F074@28fcz01J!)rys*9dm32R+?4lp*7$u?E)XfI&ge$U>n&c)(x0G#12CI*LU%v z_|JG}ewf|NzRJGIM%i2JI(8>l!5(%iUP9l!?7!I=th?iD}>~;1L#@-w3KiJRNchC=?urJ|e;Fs)I=!ySie`PmeO&eg_ zur>{1?0p@*wG%x^bDGvSqQL$IG}z0qc3#e|WLK~g>_K)lDDel_HSA;dH}>z~`v`?b zb8rLm2tkL9gxuw54klJ0SVVJ;2YQtNihUAK<|#atr}1>2!Dq04vj63od?weqojbS_ zqRFx$PmCzF`M9f?#S6F#T;fH1HZKOPzm%8pa$bQw;L2ruIc^KucsuXlD+IN8HD7}pg-%eC z*Yb6OuKX=fk2mm*?6>R``yG3my~EyTZ?gB;ee4)}l0D6yVK1|%*m3p)jK=S?8*nf1 z3VWV?k3Gk3fe?nT@NUq`?_qz3+=M&W{g4-X8}`yKvy-?3c@gyI&7eE?f^xlu_k(sl z0GjnS(5Z(&r``_Q^iK90Xsp?d8-)?R7j*Ca{8D}yzZ{RKuf*NP)%*azhCRX_<=5gy z=70D>eu!VsZ{RoboA@Zdncu>1<%jug{C55&eh0skAK_o-ck#RVSG47;T3SLzh6jp^ z>q`y0OxR`h4aRqMNZpX9XV>=O5RbI2b(?qf?d`LAgcVY^y?^^~-?k8sw8I;E26pv~ z3~d?g+aKN&aN8Psx9{=v^z;qyvGw?!*2W$WvbOe0s}bdR_JlMU1@=kX*5r?G>+?H9 znv8<`q-|}IdGrY@d|{yI@cw|?w$Pt}t>5nyO)V*vASfv<3ttpS5q?R)tu5N@*`-~A zpOD2y9S011u~C}=X)BADC<9x=mju#=ZwRWWKwC z@~tpxSW#*1kooSC)(RPSx3I!j1X>opJKz>|DlMrq?0V};nRt(^+bW}OBht36lEZ35 zSe8}028R1BBlH`&D!87JBf(!Gs|>i07^7gVtl4G4vaR)ZvF$Ry)2L6CVOLALtWwxz zb@IEcx?Fsh%rQpAoHA>Ztl?tA+A}cN+o!c4U$tqt-`Xm?wUrnh)>R_Rx<%`@EZy02EHbAaKJ9tbj0y z2$&2Q;19GX;Fs1|m7a0~Ye+#H=}!Vxh-+Qi1tP7DeS^*=jm<49SwjD=zHKaT(6eV4 zSb;{^3Cgg*vZ9Pc;2Hrx2#y3^l7w=TnA%XcQfE2KT3d8h)zP|42i_o4Lb(fYmt!1C*1xp3{ z6V%A2HtZi9~HkhV)wijyD8xxH(+1A z4twfh?5DS3UnMN&8sw1X#~-tBdL(SG1Gp1m@B8qoBIt{1eF{G#((1`8oruH zgE*q*5dIJBlUfCRlfXGi%PqBwG)8h@kTQg7K=lZOkYp$h3{}E_+J{p7?E@=1QY0fE z8hu>k=0Y4B)A4IX+{=-39LDEm!cImU4Okxek(=t0G@*VRI59^k<%YMHB2E?{fm1kq zxtL2bgvyRLz7@c$1K+X^3UKWxU@RugC&Qay9R>KBu$_zx9tdhprf3a8MFW4g0*sYh Zml!TZoI3*L5^T`t!ca@8AE_7F{|CV3TfqPT literal 0 HcmV?d00001 diff --git a/test_proj/addons/gut/fonts/LobsterTwo-Bold.ttf b/test_proj/addons/gut/fonts/LobsterTwo-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2e979fbff2caae0ac51c9eb23d63d3f10cfc3b3b GIT binary patch literal 222040 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 literal 0 HcmV?d00001 diff --git a/test_proj/addons/gut/fonts/LobsterTwo-Italic.ttf b/test_proj/addons/gut/fonts/LobsterTwo-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b88ec1723c6cc760834716848772f4ca035cd5ea GIT binary patch literal 229404 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 literal 0 HcmV?d00001 diff --git a/test_proj/addons/gut/fonts/LobsterTwo-Regular.ttf b/test_proj/addons/gut/fonts/LobsterTwo-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..556c45e7d1d6e89bcb94c012d14707cd97d4718b GIT binary patch literal 234956 zcmeFa33y}Ib?5mWKrAFc>>zd!07-xZ2ofZ?SCPe~NETTts&-YCRAoul8cVWeIZ`QY zJ8{Rc<+PKyv)EE6on*QvmYmM?B$KwnBJFI`ZOhZkD9*%I-2T%3;>1?m9mg|K+p=R- z&F{SXu&~HlSX!KP>(h6HTaOR-J>2_0%RT4Zb2VMlv;hBW(K5^X7nfSjwVcqb#ed7y zk>$fTANxmN`QJ|S@0T_0?vE@VJ28Ls!E-;WSwHfGrrG9iJ~lW!^6BriX_mh|&;7Tb z+<)lQ!+-YpIRAcuzklVe58m;P!AI}er&(j4(ll$!TOWN_#_~1Cf@V#Bi~Do;yyM;n z?WccfT(c&BU(@tQ?tS3#d*1f#f9;^%uUWqKsr&AF@Vvi&`F_pv-E~dNwA^?19d~_a zaO1O@<*$F9?~mQb1;@9opW%7O`FHny55DXC=T}pA@%R70^PhO&?QgxqV*llHn&m|& z|NiiUcbtER^<6Fhi)Q)yi`<`i=#B^P{wF{CThD0L&JcfJf5+P&dDq#;{`lW%)^7R! ze}Bh2?|z5#**|_i?>oZ%8=B?PpIrKBOWE>GZA{yz{n4h@soAw2t);Z7wbJq%tua~* zXKBtwqgCsG^3WKx1li7RXl}kV<_}D6@K?XqO3S#plYe${b0;@6?MmE4jk_HmO~f6 zI(%C;Rw?)D_XT41{)g8?zJi5O3!6OyB z^OcOg(VFn-6PCWEvzkRqT>5GKt4u?bG5Y0oEh8fWW%zaN{HpfB3z|){zhsQ6jbX4c zsy0T|#;DrNQN6&o?JsR;POV*wG6ia+W6y7B>H3u@9cE?Bo$>@3^NkCm^r3h9=$Q5EymDUJyu6m|KUIAh_-ih3>TB}!G+^@jxj(F#jAsT{%7@fAN%zK z=EzL^+%JCg(G?j3%kNqS4&5`!?{B;G)7BY&|3U4z{@AA03(_hE_yV-zM$1XdWxR`% zA7Opo7>fX75nwC=j75O42pDh-^JS+2$4T2_@-%=xA2YP zr0SGzFisj4GWg1-*vIl`o%_;1_j6yq_-y{pp?I>rBRbrdXbZwb!}+itPk7ytklSFR zh&wjBdVDa@KTr;|cXlRPE#7GQ;z>9vKGff9`KKP|#c%Jq_!mnCZ=Jeu1pK(V7ACXD z&o($K8ftfVqyAVS;jtL3<*-}r4r{tQQT)|lEGl7qpj zqw9ub;jf-JGX84hq5krx>ofLwW5$-W@4m4!R-2~{#DtnVHDd>4#`eFiGq#@@lgGL> zY2Ba>3ln@?^k&1-E!u- zw!njK<|nN1dE)sSo#T-)bg!~x3Qg@Bn)GMVk&aBJBa;4=TH{sC|Iet-YJH{a{QLuw zz_WD;JilE6&%bW`JPQ}kzX~qyDpYT1Hh+Em?h~(S{`P#@SR3rK9Kk3%q@C2~H{oHs zw%1_N46RIL3N4BakX=a9ktWnL#k%lOI|Um4>o zW5$0={9kxG0;?T?R-j)>e5QKlZoo#r)u_Z&uFnHCEU z`b*Gn0uas7p2X6U&;48zMX`diKB^37!)&e&kBvt>)-Gc`GB$og3zY8!gXv5t)!yRt zccg=n?pS*?asz7?cV8r4O!zxn!@>4IBIAvYMqdqROmSbf{+|r_KIZ7OerjiNe~ae8 zIFVK6L0T-;7{@(~nWl{Toz|Fg!v*@4dEdoq6I@R5s8z@?z*}j=pgw34T7gbO_d}0E zAA+8OJ^@{TJ_r3N^jFY#Aq;Q_;BItceRy3v$gAIa{c2+g>>M|K{Ti)VTDw&sH>xmX z#f#2JTTmK>;jz(?d{@rSQtpOi%=`O_z4=fo)Y?@D27{qcIQ(kBqmNe8=|N94?M;_+ zalgMa>G##N#u@7`8HhTiUD_E>*EAY-g&A2LiHBkX2rIXSvJB2#{}i8KVpnfqVzCB5 z`J%BfJEWb^77bCj%C*Bvgxrw02ze|bZn|=g!i+A{FEZ$c=3Ji)=LTXOeT94=7q+!8 zRAar}xsX5Ud{tBbFMXM~udTf;YU%H9b+xv&F?#+>e`EOtbkCyxiA{93P1d!@ixD)> zsL^7jug%b!6GxSX$k!10nCei=S=fG&#tF!R><=)2WzF$Y%|jx+iHC&SR_Wvngw|?> zv1gDDGf0OSqyxR9k-~h62Vny&sBf)nBj98FdFgjuTdwPpcF`2=O}b=6E{rJEfx6UE z#*0t#>%_OQu2uNDjc#nHiAmaF+F^{^9=_CW%)H!D=aOEVH2wAAjIsUl1$<7TrUYg28p_I`tXxA`8I+Yl zSs9ekw}G;06fl;Hyvn(599JQ{o=n)O;WVXR5NAV_40xn$;Ym83P$HkP*W=iD8|@uufuFCo!y( z7}iM)>m-JC62m%)VKo~@Zx#udMFM7#fLZM1tYP%}7`akiK)D1fE)h_h+}VNyQ4k{j z&)}C4g@39Av2%jzRh%@UP%ktA9e_?j_d(~OC!i;xXP|ZHIj9{`>jGKH8nS38WYJK_ zO44bPPLp(+q|+pwCh0Uur%5_Z(rJ=TlXRM-(hb7x*S{g8 z1UWOhA(cE>MoK+e_QyZ`miPTkbno4V^82glXv!0Fwt5Hh<+)-oId*e(?quTEZ+*+N z>x)1A$ZDuCRmkL9TOIw$N_o$n@18&UuGOx|eaIJM{;e+xtLy*j2Y`SLtpr4ThWaW4 zK54CMf{H;wMG@vQ#LAn<@ZwM}!yH9M=z1|?#X))x+_cqwmLM6VpM%x)q{ z?WSp|v6>Dly)s2(6*5=7LBv21Tm{i9LG(%xy%I#P1ko!&^hyxD5=5^A(JMjpO0cdC zzDwI&^)@=O!Yvj4pu$g;LA1x0xbO|gbpKR%U`2q;11mhR!UHQju)+f?Jg~w8D?G5m z11mhRg7&ze6*MP?Q;eTefDaADHi2TBKwV9s*d|bH6DYO~tb29Ky0%(ZZ#hwKIi=nR z6dDH5CNVMMKLjJRcuGB}#bw z$~rRQ(Y-qU1pPny6WzglHEQ#C+Crg_E7+0hjKrOu^FMJl{TYk?=*iY}_~4IqWFv0B z%N=TUx5hmFF0bQ0`?J4w6~)?i;+(zetjo*K8}jm#KM;9YQ>wxaO3^CyYDmt3H>GAn zYAW?Q0On+@T$;45ThXd$)^$kI_g^a1Fj(8r-?q0c~= z?>6mEX#6E)L9#`8g&xLcUF+e229699EhEw`)8t(kkP8E9<=28}D;jT^LG*ZPqKAeO zJv5Z)@zB2q(c?k%co01vM2`p2<3aRz5Ir75j|b7?LG*YKJsw1l2hrm}^muBb=f*n` z(G%sR#aAzY;GGQ^==D{axT%yCzQvPJU*GBHf?dACXwDm-x^1p}bf%}>AGA*|RZcvz z|MvGE?M_vfis}8m4_H3(%I|vIlefNO?CkrG7m}lghElz8Z_C8|z{%Od+}nO~W%-G> z%_q}k%ll9deV4vzX|;Sy+pGV9@-Vd-Qy&KoIa-Dq8ewKfnAs6#c7&N7G1gZjh9Ys| zSnAhoXs&kDO26^jI!O;P7yns)LDwrPci)|ra#5> zrZ$B5|&UFoX<=5(0N2a2Eo1A#fK$qJ)qrAtXu&i4sDh zgpep9BuWU0B4IQ`qJ)qr5{#3W8c1gP7<5CDbP8lTnLXE|-nX}TcMH zUU26HcV2Mk72HV}(+iwsu$t4M!nJg$d`^ia@(9hWLFFbOTktEH{kdQL)fX@RhONyO zD`!KAz`lvh{`*(r{l)%RtUp9jQ>WzvF2~ z!Vch3y!1`$9AfBx%k!JsS>B^+h@l1E?V!;Lam_<(gPW^b$lyrH%d_L!Mo6{t#~9QH zO+qV>*vfKUZrT8BXI}5VTf~>P3>nzARYe=-S^fw9kF5o;Di8jW+PVi*|j0=2a zfv+rZd4aDi0LBHtxBwUz0OJB+TmXy_M| zFn`Uu_O`lgn-Lh!G{G=07sx#o2Nt=hW;&oaur&Ba!*$-uHNW{=^V*EjX`Y{Sr{E^b z^5{-x_)frnr#22PL$^TpLXSZoggyp64gC)ES?G(<*P-t~22>n0kbIJcc_(jkCnyv} zEXi~dc2*Ca;iZ<81YSY{FV!UQ5>S=QLs`fyF&axibqT000o5g-x&&00fa(%ZT>`30 zh6FyqFi4sUlAj-QxF&&VC<#nMN#MhDf0*tM)BRz(KTP+B>HaX?AEx`mbbpxc57Yf& zx<3pj9EKAP!wH9L68OeD5&eF~90QqGH5sH9k|{X4S1+ifTQhCN5lNGGJ2qaGXn3A9 z-C9iiIs~_HACkLZwM341GjYG(>h8#q=<UPyUe2&( zHV9TLt43KN^TMgqwMEXGmPo^#5k8nj-%rD{vdGyDv^xxB8@|O|+a-S}uEyQlCd&J$ zVEK7qxuE~`KLA(?7)K0QzhDAX!P1D9&AkcB?N)XTmUHH)g*9p2AVAGE`K~egR&$BI zdzhN5cB?5sKM7I~XvZy?W~@p&yX0hEanZJcA5h^&WS8XlNUXM^|PYfzqwcX6X;WkvB&sV*^laR4^K0E6@`_S`hO*vG*CM&;_b?ay+dAlj; zMA!@x*ZZG!4M;{k<8%`S;S!%m$*#Q-`Kvc2)Q&f`cfVG=_>o!`Y(2I121K;w-(Q|l z`w!2K5XKXq$OhSg%1_c{N9=o3VDBujr?DgU-ME-0g?RUv{S^7bHv-bfKF!j!@6tZQ zop#JJDhZx;auLL*YG)lA5y>pEq6i+bh*}f1(uhHQ&?K}1ordm*9)~^zJq3LNx&VC+ z5|3D%PdnpcNcw{;#tyRZILH$0AOi59v6wxG5S5UUNO@VzcJnWh@*Bp3sYMk`56epFIhGrl9Zdu)-T*g*UImGI4cZpFPsw6|^O9`cN|+-C^+A)+3MAR`l65v!NA0$(vxLBy zEPu5xw*|<{`)7c$Kq!xKwf~)Lzyuo}HrW9zG=pZKMW78VZOs6cBfh^B3su zMd)S7%xApuP6UYC@)_$e$9p`be_3siCFsMT`+<>ckYzI<1FQUCs_kHq9ZI^zk#6yt zbc-|Nab`TujK`VrIMOYSbc-Y1;z+kR(k+g3izD6QNVho1TGx8%U7(XgPfNR>w)n8+ zsgj)M=W}lzF>V2fTgf>BX=cu@Y~d~$Z!P{#d4Hnjd{5rm&1Xo-yAblu+(#n2OQKLO zGyxrePC@rU=bt5}y+ot3mu^WU zTO@f%do5Ve4!5<}f)#Ae=O4Y$1ats81>Faohn|3*gr0%cq356%pckQ+Av0Lf4x}YF zeOs^s7iA+-3Ib~dfwh9bT0vl~Ah1>tSStvu6$I7_0&4|< zwSvG}sVU1F??fyy-Cy5DA}MLI>De)W=411kiUdvTq6p2x6o%eZwpwkwNmF8XIXZM` zYwI@^^__>i5A5l&d1B!}ENt<3-Fy3o zR)(S-y?c7*SDpI5xcCGKD7j?+c)X+3)zQ^u^E!jA`Bb}~1F4=+FrNy(HjHKI=pOA31o&T~kZ5n`e?T#S9s2~P zOQ-gIF;|=@a}nayRpP&zwzAMNG*cTIqz%#Ah6ZUvgS5e-ZD^)8G*cUzsSVB4hGuF* zGqs_a+G?7q4b4OaF$@+jPtXlxL3S`D;VlcFX+DL&j517y@nP;cZ>S$r+cYk0vuJeD zM|zWjvdIOTQc!8slI-o-z=YAY7;hl&V%5^L1QuP!e>HoeCNwF4RrOn*c<_Y>$M>~* ze2zG|Ed8r*sk%aKFIfKDD9mZmnWvZrQX+)`>B845yi0E}L;9by*aU-x+AK z@f%?fQ&l&1@KfK6H&B~5+sqbQr?;6>9TENRhZfeHZh!0ErQu_5n{d0kq(L=4QoPeUM(^bl7+s3B{*!%;kp*%dyOS_R7N7&G!mu_+d$2f z+%eU3M~__SQ6Ta2djJYA&00u&k-(+EA7x*xJXEy^p2!wXdw7es9rcel6!Q3v!JYux;MLvZufrHn!Jlfy z%+}6GEJ)F%ZD`C9*WW%0-~s~s#>InUz9l2D3_Hc{5xMwvFC6T$LOYxT;o%*}B3AN~NA{2<;?{wtal|NsvsxG^GDH zs$^WR2nMgbU`F;ZdmV$X&2o7IW#ZBVj8bj$0xCx0VuR+ATp1>lb!B@c!(_`cSRnQW zEPf?UA=}M%v}-R{Pc_>{y{0;ERTB!U@)K>i*iI174Zi3&H zVDb`79{Y=wftSF*BN-IgFN=W^!%+ea8EQF{j6ssC;J-b}&Dq6j%zF6U=wG$P)%YK`VV z?HST7zrUkJtcGG(`xtu_nNOKhfq0eIuN5G*A|HryB4~8pG-3QaFV`Hk0dg=v4hG1< z0O5621LRqxZTYkzR3@zQTzEFIMQE+3MM-&*TGcJYzRhK}DYL6pxUCDPijiD%iyPhz1d z3!$t2_$&U@#wy1g42uB5(zGIM#As#QHFFCZk&wUL8$vJAN#=HEsW2!dHjxjd$N0vSN+|7y+c;pmg+12(ehit zV+)<7RKPyHzwgLgAH(=w%TLp-pCF8l0bJDo{U)JRn`~`n*ktqmkkJwscn>Ygv+#q3 zA6_FAIObWbUiDkVJ@$|prTebH>F6)#6?yU*G1ay->r ze)xRo=-FGMsCt zN|)Bvm|d3kJ2U)G$@2cHOm8gp4Y^4zyo5HQcfPU1tr9iGg_*=k=@U84uT{3oSe^4< z)nIRuqSk`65v%d~B&+hW4H3DLu)l<7`;dRxYfOX0ObzR3OF6uqC&qDwa(7pstfzOV zXBW8iJMda<;L;X(PzlMv$|OFj5;q&NhOaZ3H>n2y(U&=7B9&j5?`?^}Y>oCDHk%Caa(-7phGcOh`5`x+-_9HW|H#SSy@AMY=*AG;LN*^li{OiWJ1#wxZCe^{qXWJFJ0d?4O=O|DCJchtBp z($-#2i*mZ$*2;%(-cz`Fbw0jiZ!jX>c@g_qNV3)Qzg^CXKN!I;`#F^gkUfgc%3$k$ z&Tag8xk8Z5jc)?oR0uK{GM*K4&*!zkEvcQ#4lxfQGEUpLf_iAPUP(Pnh~>C@6`>z9 zLPrKT)rQY+|9t`HEcTF`MNuFh+MQ#Llf2~HE7G8hV+soc%3U~Bt7IbQ=Uj8V<1;F<=t++1^4?#6}| z&8E=W*h^{{=R55iRS#+G-@FQh8{0VL<(0K5uADak-DG}m-{-k&ncE!eY~JK~Eqh|S znSRIixaiJ6Z!#ga8SdZMA#nQl(SDe&+YkG;h-kxpsPE&v3ceMpWmDsuh&B-|gP{y% zwbygkSRR)M^sc4y0f|uWjMS?wF+a)yUCp+ww%_8}SsuU9ZjGI++YNC(8!nX%3V=2n z6-#y3)m70t&4y*G;hA|;7PA0W{*-1{2*9pAD;`?vAEl2SV+MWsh7JmhB*}C ziY}5?Y~W8`*$%#%#KjK&v{=HvOBXEv!t(#JGFs99LN;RB*!5QP&rwznkI5o|Iu`Y` ziug|U(Gn>ZH@x^)_nmCr3uQXw0yyNAw*jPl;qmS?W8E30`|?|VWOm;_e&B$8eopUM zd-oZ`p?C3nOZP07+^>A3ZRX4apj9~)ehH_-3E5)5-J7i3a8|X|-pk%k*+4}wbtkX! zY5sUW^f>e(=qczE&;{sokZcf{8l={EBeN$ZVmH;~S|{ct4J*56tz0~PVDREsDc~!|`23pX&zbgP+ByBtHyKSE2@N&% ztR@xF?Yt*`*q`d-};k6V{NCAZuP@vd>3a_Qm=2B!=YD1~s z(cDb)?RAM(lxZ$DO|yBXv@y+-L@LCh*c6>=oDCZ443v^Moonm5*oOnt8Yt|DU2 z)Ix-3P{L;j5uPDLc!pcf5F$K7i0}*{!ZU;j&k!O!Lx}JUA;L3+2+t5AJVS`^3?afZ zD!AgH!4HF$d;T{dS=CfVPWYVR0H z6F0~!Rqt}8J?&l5RLs+5DEo&;0>yGE=(GnsgHN5j{rqAqIeC0CA;G(evFe$H!t8xN zu{3!0mV*gzs-s=kyHbI4KGf!_hY()*s@>@i`n>^gQA;#Er2n#%eXx-SAdoo-vK7Wj z;xPkW2OGj|o(0P3gmpArXn+ZP$<&J|YaAz?U zywnVljn}4`Nkc)OhJwBu%({WT8|b@%z8mPffxa8)yMewN=(~Zwn|E;oeK*i|1ARBp zch{hQqct+RiZSWhz|9|lA||Ox=K-(wLLkQ z9Xv5ti1tr+hx_~UUVFMUp58I%wZ(ez$Bm-sxl1qU|HbmxEQo%7ldKAxc^sM?#fP|3 zsf$BQ{}5;y0xcw-DYOiMmLUTzL(JZ+QGN;ODfv{ZeDw@us@kn(mIRHHd_x_c*3a`~ zcL+0QM2j0KV{#B}?VPhdT4ar&A7!m%RXd`s?1;9?Gh|1!6^K}Yh!u!ffru4|Sb>NY zh**J$6^K}Yh?NIffr!;Gco%sO(tK1B0u6;b8VYweUI70!s-h3ZLIgn7xm9(TwOkfb z;+R>Lp1?_;11+^;5c+yH;R&{Rysl_>EZ&#!3@#m88tk1r-e0(BZ;vO@m+0tpM>_+K zDEVUpdrR{N=S#Wqm4biIWS`}(-u^aMTRtpXrGwf2SY@iWd-TY{-V-Cy0L61-BgI5C zs|Vugc8=w1Uz(qo>(BHrP7U8wjX9I!sA>aNR@oeWNPqSR39JpI&7`?=d;Kl!L#xY+d4A+dHrpkr?P1J0+-ihJFl-t-# z(qfocl8qh0%nEUZEyk*_k{gxlM&-KEf^Jr#ZdRghR-$fJqHb2AZdRghR-$fJqHb2A z?%GP!%}UgbR#e;4C3~}`TDR>s-&RMdk@UVvR*!5U5S1(2zSuXc5~W+QDoM6iiPEho z-By%tD@wN&rQ3?qZAIy}qI6qPx~(YPR+Mfluh5FpZN)BY#V%{bE^AeWzmtX}N|O?4 z?0!b$q3&2WN_itWv%0bIM-uSHWY>#jsry*@0avi?`00cvR>}l2%MTyzotvEKEKUZk zmPb8pue{Up%a-ZGd-wUgx$(ZfoAz`&S{?1qfbSS4XXw)RT1rgCDGSbnL2L)*_$^=- z%Nkq?zt~vnNb5K)aV@NC$C)I_uh*o|+A0^%K&E{Y#oCTCE>T#VyRmPg+#TfZpxVwi z$YiQwKVy6@K_{Vmphuw(Kp%xZ4m}Hf1~Ruc3^JKi2x2madCTKWrnos(i^>8#WrfVU z)%q2AOXK)z!UiacEWfPlm-AJN)0-_+GC!Bd`fRz;eW^gH%n8WCX4Y<; zH3f2^|IH=_w-XPPFjeEsWyvq!!!;~;J2!Mdd1wq;f=)vBK#xKnfIbR+9C{Y|3}iBi z6(*5&z=rXJK1N)5{TeyX<|evEqovfD;Gc!~r;Q08Si$69?eL0XT60P8@&} z2jIj3IB@_@9H?>PK#dazn1TU*20Q9h*`+Nf;cym*kjzEZK9a@_;w3|HQVUWWRoJ|JF)mmKLW?YLP`fuRLP;HH-Dx?KR1adI*-2NbxRh zP;cF2N`$usUa}1rmj*2d@am?ecEaKW7MO*BS(x7#W>~|(EDX%Tz$^^R!oVyH%)-Dd z49vp7EL?+G7?_0(n8kULDh?;bFmuYe+>J))u4#mBG(tCc=td*(251D?3f%X+DhD}K z_ZDv3NMQog@D_?wn1wGtq(sYY{1B<0skU2g5LWb=>zcUPQZ5NIu)4VW!k!`RfO}rjHEtom?vG zziC+u-F_(D8*Oiy9?LIH^|wEC@pm)x<2{y(59cNi@{Ybs-?jV+EML}pRmnj1CGh$= zhP_cIPS)|pOuQzg0;%C)aD;65YZO-4Hm+&m-gFaxZ?k>ua)({-z zn5(F-~gP|QXOdn?f02T};YZPy)PgTu@ zM4_JArUSp_Py8cqIdtgku>auEHn-an>g@~;pLqMUE9493`XiD4t}uI5{ye!lUoOwB zr1Wq5?>&+##62zER5@KaIhVIsU-_Dd`PS6_;XHkBI-8|^>I_J~aBX;dMeMlCvF`G%cd6fJfyCyA!Y`rMbRe5$$mrR$)b+B%W zxz^mpOxJ4PsLB2$lRe2c;a}Z(y5-fSOaq~?RvIy=51NElAUW%#pHH)JyjkSnwU*Jc z1y*500tUvbOz|pHyvh`>GR3P*@hajJRKJJEXMl>tV8q z5ug-A!wA@57hr9^N6nU!1mmxbB-lX8Rg4tRH&_lx`I}+nfRt$%kql;y3|-8SoMG_nvIC0xuqwynU; z@)E{^n?l`M1h%0n3{qQsP;!))x`r!-DneaWBe$u^RYC=vfA>!&pLzQ)^p1A6MRN60v+TQJzef_NUzGYN32|y4xb*XokZ2RfZy?{ntifkuemnIyLBu zLt%Pd8)LIARVX8Cljg8`W3gh6UaQ^O}7_xo0V=6#dD{Py#Yy!}`5BN^|u!MxWP&@|N}b{tfxXYi=yZ&-hE z;O*;!uWfMJ=Il00k2FTvR2mGKIfjkOI;xP408U`08PJn6dulByWGH_%%jZ}n{@R2* zDW78vTH1d6M+Sf9sbo3hk9cCe32)#2GehCf`yL(q@Y8zv;vehx>32GNM)&0gCOmD) z!Tj*CJy|Q4ix+?K;=6&OjM_VaVodwb;>vIu9stP#wexh7KeyHtTH>~o&^^$j&KVN27i{^P*3R4NF>R~0(HL7fsuwSX zF_iKE)ry!c2GDPeUfs!L)E=_>^?Uw#S2^h#9^I3jIXzBk+JE}KkzdtE^pCa{hNqG@ zPwibEI6Bi!QQ9BV-*fTc#s47wMj0;_v@yIhfBVLcm=q~#9=lG_R`n8^ufh1K`k69* zswLy6TJm6NU2!ChSnN)tX-8RHWkjtniiw~G^^**04}OBh=3(szh^iWHhL)t!8#cWZ zq{w5_=dtPY*!1}ISzc&-_Vb#snhJ@$M##<3rxfg$?SD1=8n3ba@Ea+=nW>qMuO8Tv ze%ONdQ5F?fgSe!0D%W2ZpM&WBD?r}bboyYk&A%fngtuAwSDVToy?n!&Q6yXc8q#H0 zKUOQ~W{jJa!Q|jgXcAh1WJ%^SmSk3bfbV&TWxUjV%hS%YT1@j-zAV)bJD@x?1}#A{ z=;HR2<7A|IRFQVkY>(7!Ix`ivA)6a+P}XT*M*lkKU5;#*Al*2$4BZ0V3q1yX5c(MO zH1s>rXCXt>eSyZ;AjxX$!*(FSj>V;%VeZk^2FGe3L5^o^#>Ac)CPx1U z#l7^@p*PlOz@P^Qh4H?u~{o^5w5X-+|1i9Tfr9&IGY{u_TDe z2T9`)(^F-xSLAUv*r5VDRJdU-iyct`92LMpNhIh1bPBo;IuAVoJqbMntwYa2FF-Fs zFGFSuK*dM_=wb0MUPEzOuWJS6KazB)TH(koJ03VZY}87GcUqVH5u>cK{wvK<_eeHE zU~r+5o*wE94;-kv6R{4*E#A(U&yk8`29oWA`b$kAcV9dgEKEelZ`n6+*U>SXJM67o zJnB#NM*hN=D9s(}I=PDU03rJS2SU0CI)M<8;9Yfu1h}jj?=PGb2Nkk75_?!|!#FcG zTtmn(Cgd>VjL8BmK{bSID?iu(gt-g0altDR!`qbCWS$;kAVhXKzU~O&=n}2d_{p+` zwp>F<8H6A{6hd|{K3?Fe_nP<0K@|)T@Mt{lBC$h=lW~0@~HAxoOz(2HP z4BQ5O3Dr`&ko_E9WvozQ+!$jRW9Xe2dWUVXXps<~GTx$3Y8}kbxq{uzK<$L=thV#? z4k!v#xMV_eBFw<~f3m3+6VK#s$;g)VMGt5FBcPpjlbH;cuFgJLc3K zdjJXNq$;UpoU<#jDNz&XuNL%I3;L@C{ndj0YC(UspubwsUoGgb7IF<*kXkKBt(Kb9 zYN<&rqt>P&wcHYIH$WmOot$5zq?S6R!o*|;QmX^0MTK_g0CWnv4>}J$0X+#l1Fb{P zK`%frLN7xmCOZsF%9Kc9;YUYooF8 ze8F0c%vd=qwIKT-j762Kp&Svl;j8Mg8WrQ@}ih=S?KBX2a7J$*1#n4&{1>Q*EPt*TE}?;gy5hZNe+EVO44y%IQ5eE=nuKwTQth zPW~oaF@xJ7?i++S}ja46wf`b^EF&j^R{k)b&u69bRXfhX5zPsi>A< zMMO-I(I}$Aim0$6Dy)bKE26@RsIVd`tcVILqQZ)(uwuEUNw(bR67r@WJqCV*YuLd(v=OWeG7)8 zPZZW3-HIO&7PM`*$**j1>ECm<#e5yzzH~8u@p~_v?CA2xLdjHl>g6xkyL$6&f$%H8 zu?w5*4vWv}^QTkZbkwK+uC2Ar-R26FQt|A?qa=R2?ORPYgUO7uz*+Ed;LUH7F)vHH zx0kp+O_*KDGSE1##z613lWDf`$lud-p{flmJDT6{b)k%= z$0$y0WEkvbg=mx|-Q_CrfJBgXw^Fn`BT*$|u}JWN#iBML=$gF3a&Zz!?0UH<_N7{H zZeYX`wKX)QplhmbwQO@KZ9~^K34B$7`-ZJ4M~xrz16fnbXnHkyfjYXbdv#fZ*Odj{ zmKEkf6Is{2%BL3Y`lXjG zL)woa!=&&4B_S@GbV8Z9cJbbqK(ghVpJu^=&;y)uVB9z*k8jrhNRITeqy5B6mEuJE zX&D}l{TQ43F*f&OZ0^UyvET4;)IYhdKr;0$LFDj%k6rdvdsbiEmdiE3FN8mxd6LV z%KI+Q53p(jp!HL%vZnC?owPVNai!I2Yb~&%QYGy@tY5{GHzy@!7Fg#jfbIp&VogKt1aM5UER4C;d>p%v&fbU*Ys^daae=o8Qd z=yQ++-^HuELfOYxwKTN{U>N zZNn`_F&^b_G28>P6)(iHC&aQR1doSU_JmmWgjn{3SoVZi_JmmWgjn{3SoVZi_Jr`< zh49>k@Z1@vmROBW%y>hH-yqHgIr21R9E~MIr&>hvu`s7cvJBG6h6Eue0Wp%@TTzLP1SsOQ}`uVVlk>S$hHuJ zcNB;RjkBxF6C1=2CzX}VO{~hZ(Ad3HE7)Xr^hgv!{+V~wwsp%EwB^*|SZ1*&w5KaJ zn2$Vkk7IbUay*`K7bll{2X2nr{mH&`d}1)2sxI|)q+C|3i-I`iLBIYJ$$Zqcd^r9^ z_JEJcvtP3G4HWwGqmuBVg!DHJA^m!+dqnoCb&s3}ri{6*>z-|gdH+K( zBc^_kX2b->gScR+b)3|fLjc*%zKs8M2R#E7{{l|k9ABjvbOHK#H3MJY}2B`$o!@Sw~| z9nd23RGn=%$>$1m8oD2P9QqLS6!Zz`0`xhkopi4pPm)vORnM|o517wz@*ex~F0X4& zB*KZhIBldXZ%TzbvS#InQG`;$7Z?OmKFf(LaUx5^f83*@={-c#dz9>V(uhL6&;)b< zItASaorj))o`jx()}iO1cKmP!1Y(cz`z5n^uvW~8hAQSnLltuxr29d-AEf(1x*w$b zLAoEL`$4)Nr29d-AEf(1x*tR;4k8r?k&1&N6{S|~jdxNjBDSAV*v}{&ViXS3K0F9! zm2=jif*JUX#>J&HgVRV7$OwECYO>Tkc*-~uS41c`a!4+J=0rge#FqEyp{`2GlAFD4 zFUL$q4(_cKqr)r1iShnKI+log5}ruXo1ns?y`x;1Up{nXXs+P51oQs+pu>9PK%_g6 z8g}ac-0H9fdeULbfkzIP`eu*x-h6*7y;jQ3k7V7cp`Lty!rqe#CIZfAVL0LLuN2$7 z_a8WLwtL_4ilwEjk1g0j5hL)o&qy2Cqur#Fk}F0oJ`sQ+j#}CHFCM);<^nyZ;F|-! zxtckU1HL)TfgBLb0pA?(%>myW@XZ0=9PrHn-yHDG0pFYfU%vt09TfZEEwhE5mBQP5`ZMn1S4@ zl}r^Q=`4#iTVas_U98107U7CTMraYPScEGU;fh7LViB%bgew-|ibc3$5w2K-D;D93 zMYv)Su2`(u3X4VzV+yNo(cnaJr|#Wq5IaoBj*U~T%*=oUyjz0OS4&)IjV04wkGC@y zO0~B*{T=CGtUK!N2|4u{qLytorf}Vmt z0bPJT2mLAZSI~DMWRC+`-sr@NNhrD1Vnc6T4ytRi9IXG9xwy)5kW`hO4Ca#<&G$f$ zLLY!W3Vj@U7WxeI1?X$gw;|*8>@+%{JYxkmX^J^IEJfXHcLw8RNV}YvPS# zqkm}0K0z~1Zs?A}Fhv3LDkQO6JLgDWu{R$|g<87`!C){H3Ws-V_55WuogVZ=)82GB z7x()+lYSprb(el0-^qWrya4jNfTKlxCy)hA7*xKTe`jup9#YO0t<$ooKW35IwpmST zeYBuIgyVd)q*%Xf)3F(~QBbBkp8KgkcBP8czp_Jp>5P6}e*i zo_S$BGWwNwMbDp)zH8)N(Z?Q(zRU8hpIzu(T)Q~q) z$wa64v|M95y%BUK-yhv72bs#y#*HcvGPK8VOdh1jCeX9A&jR%3vS#(bJhmJW zL$UihBH*BI<*C9@%Jvg0Pa=^X%lOE?dWN6)OUJ(1wX&yY?CiUjBIA`zVsN%Q5Ouh+ z;Y4>w&rre>vbBttEjEWUSV#w5@ltm3E&KD{u08#uNBy4G)aXqCYpJEp5hBscYInDK z{Q1z}p{b)k^FuRj*_n}{a|Z`JE`PLC&aUk*w>iA_-?xX_obJ->!K_pt*>}eek7g!% zBjs*;%pL7b`TlEL>ofM&-if_IN5JRu)YsPkg1H-~9OLhjWx|;36jMCHINgAEUe?hQ z4DSRUro}C*>!Vk;=yv0i-^4zu9X0wthlf+@Kpz6zl6xcZ_eK6CCm`;AnZ2`6-sQsk zt5=ZWyHrb-W7|Z&MI4^`Qip<)G%|Fo7@v zQ|jV}5twKv7VKe0uFY7g%g)3$WvB@ZbviXvhD|a$Y*)zWu<>}CuRmGW(Fr_1}A0)CuRmGW(Fr_1}A0)CuRmGW(Fr_Mm@nn!*pV1;P(tK zk8T^s2Uy<|#g3@a9;2W;1@A(#mL0syKafdG4QCSLN5?xy^BsD?lPJVOInSPCDdn;J zYtLW%JA#$tbCW-Gq!KJGj~p-Tucm_Di{Cwyosc}!K{A7mFolP-JN55NUXTrKB*MBE zy`jW*n3nQQEYspAtB6ZG^ViNOw=;k3C_JnQTnlkN!;Fs+l*w>QhFdb+lHrz&aZ3+R zST=_6Hl)v}k=t0i(#sX)Kb4FvM$)*imziBNfOG^|B_dFKBXf2r>1sc3beQUCr zQ`hiC56B$@k`E`R7`1>2(d|-oF2Gad)m2Mu7PFqBOl^v(O&KneaXy!!TcCTP$Dj{F zAA_ETeh2z2^hN0F(03pMJPsP>)T-kVL}^Mr?^89no??_lmjWgy4LPTv7m_2F4?yzY zeb9O63Ft}a8E74P4tfE45qcRi;d;t|YY#(m%J_9-^_EoPlwWLSB{HJtP_8{5z3xjf zw5f8U7`|%JrK~y~^%~m#8Z7Z@@6##Rp41&P`gqSszJM||94s}2T92e=Q|?^8$JalZ z^|%7zHh(JA-tKay`?_-uUn~$igQvv+Z&<(_sg}E(Lx)p$hdVh5z~AY)7}NDmcY85a z1NCqBjfFZAUVExE+|iN7pAroPJza(FfpB@@tvBsG>LxY{+y9N06ao;x z3@(14r~_gPcT0(4lqpx*7}hqmZ!iTHsG^*ce6B#kC`CTS2WK!!gZY1`gN2;Tkwx1BYwia19);fy1>L4%g~pbIrhEkq%W& znkWx8lKh3s%5Yg3E-NEI%Wzp4E-S-jWw@*imzCkNGF(=M%gS(B87?csWo5X`%-!2G!O)0Xv3Cd-Cw7YH8#xIb#Nh+?xIkZbRtr2sA^UOZfr z71?c?%B~GDPxe}NvP!GKBj~Kl2 z9`1S%ydr_PkBG83SF{Scv%Q&)c z!T_L{zm#-T_KAy;a@82^#t#=VPbgA_VkIeBWt@VgG9veVYT~nKz-_K;dpZQs0vVM~GPr|DDHMcQH{FF7Ha*pm^ zZ4r!}v5s49JL#=kZom45hvCIspi=4g#^>YL33Yp$X-SJ9fQXw6l$<|Nv-5VyiH{-NOXk!vwvJ&$sb;oNMQ4pOKU7xvS^AZ*CVFy5_lL*Z<1M>XRt*Wv*TGf>|fZrBeL#F3+(3iDAn(zaK?E zE?=^1@21q1RU4Ejr-Mr^rCLws7aL_xOx11l6zTF;-#t3|J?mfbovuqB?d!b80sSj- zM!bXnSgDHpa~3%>i=3H7&defbW^sSc;{Ke){W**Ka~AjKEbh-)+@G_!KWA}&&cYzG zxIbsbj&*G5#B_ho;@O!+gR8UF-C)ZN&St?`%@60Go#DCzCO$yQplm}{cT|ke3&g-A zEnrmK;>Ng{k`A?V6_sgR)W<^uv!ULli4W2Y9$Q(5(a(=1HV)=@F}#-7+83S5iJ#C zei5-G`+61;ON)r5Ma0q~Vrdbvw1`+*L@X^LmKG69i-@H~jQK^3`9+L*GeGK~VH)#` zOr6AD*R=x-+W{CjV%CG1u_!OEW7H@LBmYoVd?YL}e&zgC8CSA&H4l6m|9UvmRY|)2 zA%BZI-3vVi zeGvK>^fdH4&}X49LSKiz1DXC1Yi)AX=>;;mlAq6ZW@R3$&B`WB<)n}pFpV)HF<=@4 zrZHd|1Ew)x8Uv;=U>XCaF<=@4rZHd|1E#SWOsSE{Ly`pie*-pwB@>AEjd#s>nsh{1#{ll7zfGZ`@x8MwiH* zt|lAVEC*n$u)9n|8ahiIIcy38JHkK?9kL?~?4ni?26lvj9bsTc7}ya8c7%Z)0NN1- zc7%Z)VPHoXnB_^FG(;H4j((!yYHjR8toaaYKE#?2vF1ap`4DS9#F`JW=0mLc5Nkff znh&w&L#+7_Yd*x9uO`;OnJRmt4n!6Qlf;yn@r(NSMSc7tT<&K9vymHIlScJ~;+h6jMbmV@vo#ebnAsZ4 zl%k_6@tnJVYJX4H{;{05@4)1~`CzC+ztz#{iKKg{4|b2v_jpDpBn3D+etNEW$FYg& zJu!*j3Ub~Ca(ZjX5!EG1>*1a^I^A=a8rsouD((Q5TK&<4(_UAo=-GfPYWa8Gv!ds4w z?;A^H0ZlidrC!Q1oV5Iiq_x`3z>=g|U?~EhW@lsdMx2c$x)itLYm_P>lIswX>kyLb z5R&T+OviiK+>d- z;&?o1x)!g;g=mDfOb22MStO0*;kvCS($%W}%{3f?vEJ^Cf9w~I^{tL)?7l>Cs$g)6 z7fy;MMFi(6sRNG}-+L_&pTB#&Fn2UMaqB{2dhbLjF6MunM*2Q)hcEY*U+e|G|oW(KYMQi9Orf2`S#Z! z&{!L2^ah|8G`i7^rLpfIb`Ssw5+Dc)0vCmw1uh~*lMEAD8as~1c4T=H#j%}{?aaLL z8)rtAGtMNFmnak=XQ@;axl)zUdlSoBGD%*=Qk)m7l6i`3zeLdcoqNBpzXlr{=^>SJ zV%2e9HopG0d(Quyd+u4xjrjFi-t?J8ID#&6daHi@4wm@L%K4)%Kv#i~6v48$2s6VH z5or`T+Bl6)g7eiO65AxzYYxH)2NAV{{NW(ec93QHL6+qQS(YDUS$>dZ`9YTD2U(UM z6zf7_n+_7mIMg#?u?W%NQ!EQ(l`MUhi+$dpFgiJx6h$j!8W9VpY@ZXfyuq9k7U{ zgAD6mB|L2y<1*4#az}=z%}{uIDe=$9Y)-59p-SFqCjNoVX6a>t4~+Bf(12FOWp`ji zMKsupyd zs+l_DufHP<+zH$20>!epQypg1sy~NqTLqh2v+9xl3m=CRNY6o@^gKDS=8iZz{W6&ee;6DbAE}tS9l0R;AC?>Vb&U^tLiOHM zcU8XIUF`GuO1yzcZLp@WA~DT6*Yb{Fy{~tm6en7Hqoq;*fe!@gf~B64QeRPNQMKEX zD0f}U_L}%_^QzSD?uZvo7HYE{v*T+_&O3$LVr&R%OBX3^fM~Nr;deU@R>jc&j1anA zAu)UEP%3{uxKE@m{Pwqu2c3Te*mWB9 ztAHS37hF(jI|h(R^f1Pwy7&@t!{=yB+y&}X18K+i*8hQ1Dc8~Q$E>L~jpc{r}+mg>1& zefxw|5ozMZN%NQ{^iz{nMWl%;BI07!52Ga8%bgam%NXL=JQez!a)cCzw(yM}wQ@d* zVkID$NhmiG4Q+XXr~R~4%M|ldu4k&ucJ(#T!--}@#)H0Cqi z>GxEYRJaNYL%za-SnrGB<^oSuh3jo;v77gwf5}cKFa-h`MU)tE!kG=e}B))wCxZE`H?UU;g-<*yiGAMt?_!_Vvr=8E+UIV;l?sHFZ(TB zN23&-0M9P?u7*-?uq0JiUR~!cB74bISmG`zoGuHL7q!K!dOI76^2-W}{XSn&d6_G( zglA+UX3pty9)RO|9ka$mtK5n7UKX*SinO{_0kUyk2V5=P!UIqW>VsyWW#}Aq19}2_ z8hQqL4tf!K1$q^F6Eb5e?30)=6&}nZj}{z>O$|UPNdB9FmZ5Xd4d@BzY3Lc~Ip{^` z73fvyO~|~2eG>B$eO#hXE+KUv#xYP$mtzrwYN!bsgl3^*&?C^}&_|)qKwp5KhrSGn zan;F;9FW|%A*L9|3@?lpji!g2qRyuKEuT>-f0b=I6{i?vy0Uj~f;x)v_8hmQwNeZiVwtiHN=;9HH;Cwlu%OgA=6pXig% zx4Rx3tFIq>u&Z-fK9_;fR>yxd{+-i<2I)4wCQWK6{GdUv9nizr*PwUj#{Sp&@wcJx zLni3iClSNEL`6gN@Va$;&GU{V-%JQI<^~cj^W8b<2J{5qVR>>w(mHFwc7@#Nthco1F=5&vZ=`<~l6+#Xr%?rkW|IppgB@U2QDhP;cMFNYo8LuLeGSN9^V+z~TYKADO zpf}>Fz#RFpg!V}6orLxjN*Ol^jN(7yle+)*>8EEu@rhaEi$C(#bI$!gVoZvj=N^8R zd+<6wB<5Wqm@cQW>L18w9;cHnt1|P`lewo#(G>JQ$*2x9dr{pJ4}+2u%fh}pF;OM( zNghp1SGij^pK`Yq=H2=;cigM)`g>3FKki%K`Nw`_?LQc&e&l0v_cqG5ImV3tjV)Ce zznLg2oK)DK4z^T-Xs}%hgCxYYmP^&jF^pRo%V&>#2R@BheQ9!w7l>IalQN<9H!R`G z@tm1&!}grnuC!I|)j+4ptF7c>RICSCuG(W#>L!)?nT}vb93&#^{XSP7*_a^3YvANl}VR)Ut zg(06b@>#4h&0%dKhA|PtmWdd~+z#`+Va)9?=5`o!JB+y<#@r5LZig|q!7!xs!)fBc&MEfLSBIYX-QC7zyVP)`drDY;kvY4o3QY$h3D_KlbvY4pEM6AR_ zti(jD#6+ybM6AR_ti(jD#6;As&EDkGG!gBSm?mN+CL+zxFcC!#8)O*}ggvFaiuhLy zmQ%{)x0+h&QWdV-Z2JFD*HTkb8g8xY9mPbSCOi&ZvEaw#qnEBz!&C0S)`|En^K9+Ga zScAV^`kO|~JYNst`5J{;=Zs%n#qVVmyXgY+WKkkJIQde95LA`S zV#mu6o+7F-IB?_W0IM<>rwkD)Lxjp;oH9hH3=t|rgvt=1GDN5h5h_E3$`GM4{ZLDbS$9EX?=sT45=V~)X?V=yN7$a&CG>b?l% z*2vPvZ`v$mkv-lvj&I}mHW;}LWN8Cg+CY{zkfjY|X#-i>K$bR;rH%P-WB%JfmNpAn z?2`zx$Wl|^VzeX3zM3zExH6q8vQJyQGAX@+B4Xsf$mF z+i4ipI*G~@$LB5X)f(+LM|gO|0mpg_2Z02k2YXG2TyM9Z#Cxf9D7zqRe>A9n;|KC5 z`sH7vMjqa|UbpcZbpuVoh_5Q>DJ!pvxC5=pptmkv-4tj^1l$$*zOq7BeX6RfsV3Oi z7jqvs63KriBvU2DxH@uIm@b2HI)snlA`L8 zn$)iU?2fUVi?-EP8c%#b(dB!7SFno@CdOn%0CKD|+#p_C?gqnM*j7P9SWb^&Ejql?LX*Spbm4@E3jVHKY z$hyRI?Zx;~m-Fc3a|RMnjWP3L4AMhZ+)xSJPzen_MGOcShIn}B;0aBl+cC4~Bf^(I4NMuxIH5N02BEUD+YE7Mde1f2>&r$VH@5OgX8oeDvx zLeQxYbSea$3PGnr(5Vn~D#TPO#8fK8R4UZwii?41Drv{1m`bIneDNaVp+RY+EK^Aw z(<#eTN`V0>pfv@wrhwKI(3%2TQ$TA9XiWjFDWEmQBc*`W6sA%NQz?b1l+xEOWMJT> zFr<{SIH9e&wR+z#>ZBBf+6{C&jq}&f?ea$({8A&itZj5(DlySj>usIwlj_Oe*!w_D zRSixw<2^GcOK7B2qU#@5^&cH?e%I1KNu=vnOAZdE>Ke!9%?ZE4gx5J*WWwW2cvWV? zlh%aCneaGw7zZWeOn6*PczpeYCz)`whHDW6=@j1t4MMZfG3XKKapt}Cv{{gC!zfkjXX@FHQ|lCq(=3hSjhVzDQ$8AdJK9J z`ZV+`bQ5|B`Wp0C(DxwIHU>i0O}H-RWoLbFIOnlJuVDlmy2J6Q{`lr|9tgS0@`Lpu zcXj{Ku??o(3S*vk9C7wYi8Bdq6geM52A0(*JCM2e?gE+h&)DQI;Jg7S1@%EQ&@yxm zx&b`_Jq;1hvrKZ9NzO9KStjAbTZA)P_csYgJ^%zgfO_&V zK9&hN%Yoy&2x{&SHtY~Kd>joME zRej5&Eh`TWl#;l%@DLkvk&@Dy=7=%Tk{X?FESVdlPl3ewJHJJo|Gx-G#(QHLFg7eV ziaa!mJeUiT^#ampM#JCdsDE2P8o}>-UM56Rjdj%j9SF$!9*kCS&t@X>`kC_Dklm$p zbkI$>x!-MdU+mbE2u{v#A|Ip3#}k%({JpPzeQVO|bMatf#FOc~II~dKzlne}MT|*X zK-gO)5yr0!hXW+Td~KDYquHi|?&dpaF2GhDX-3MN5M|&1#vQ=80~mJz;|`R817+Yq z88}b|4wQidW#B*=I8X)-OBpy&1`e?)MH$R-mO0Kc$64k$%bYSw<}yZ!WKCA7xY!9< zeI;3aDf2{DU+c{iGd*CN^;su+Xi%+aB*IMMZEsOpBdJC=9civ>=g{PsFlJJ+Ej+eO zlOsH``nujE%}p79~8a9T3ATK!^1jppZco)Cdhg`=F!H1?Vy8N$Ativ(QcG zB}if=r8BK=OeN0dsh6!~sT6vX+9IZTb{O;Quw|Yd=1PaT(qXQ2m@6ITN{6}9VXkzT zD;?%ahq=;Wu5_3y9mYI6jCpn#^X#x?o*mxSJlko%$7HVQzM&asoLHAXuu-eWKg_Z2 zH?s8Bp4&Q&;1@Fs042x9Z{y)-#Ye`#FTTTTKFJ=iTK2UGO2AP}lJbA$gkQh&3qt{c z`?4?;?~uoe@!x)lsJiU!Y?*jPg8a8%DjMIAevRTDxB|1VT6A;>v%I70MR_Sn#Ui%= zpQ?4tk`tPrgUB`j-*c=Lbo81Hf$zhjQr?EXT(&=9tL5c7e%TP4GH!(%+5EB*_+-2b znq~A#^K~(#9S=FL{4lOHZ{gcuHLDDQhcatXS%03$tUr&6t$Z|VFZ5n4NTn#MPNutW zCHe3N-~R=ve$H%rTNej9j&*Pxuk=G$g>nq+ziqKS$&}rGnW|Ezw#}8@@lv%@glLgA<-r?3jkF`<#S!=Kp3-DU*Cb!xy`X{t_5GIr*!!1I3Gw7oh4>|VW z77TYE>_OR8YE|0$zCcmhyxfkMOLaor8Twq328cU>9#K#!77u>|vsB2M-uU%ZiN#=+ zWY;0Zh$y7=gg9yjaMTcmq_@cjzz-dkG$=>l5RfBvQhB~EMJ`dJ`9MrQ5R(tYiX7C{7q`6@m7$r%O0kc<&r0SHaISFG!Nd8G1|B|qH2^W*% zYEv0=&aBpBhqh(!NsOAjzouVX^)xgNmetRQbb4YQjcyrq(sn&ZcrmG?(j z8jau$8R5lA7vB-=&=I^LBX~nb@P>@w4H>~3GJ>Wa!5cDyH)I5F$Ozt$5z8AgqP!tV z6&sDgq&DqUR^n|f3i0quC560E_V?SUC0F357*jzg9W8&XI$i6jsBH~*Kd`&@z;u64 zRmV(QdZx3cv$vryUQs)=+!v^?Dk}}v1zNk)lhsW5{|d* z_#5eyAiN=b7iA>doywoMNeoc|#|NMk)CbK#W^hpzGf~B>u7dNb;5;HhP;OB;C7IzA zvz@T*B)6k>OuvT7OX>)TJ<8KQxjQdOXolP5;ggd(C_0)cght73D!th_ljZXrEBxl~ zt-;9pG$8A1Hmu1HdCm9*qm}EGnXy!o|3jV?0X@bqNG#@SGv;CAn;ka3_76rrhsmbP znDIOKlm{JqjPI+s(x6W@2_TF}ssmzWOrmws5Rz$RTu{LfDSyX< zG>ooKCQ2giSlkya+vV~Equ#1yRasTA#9taGM8euU{Kme+ogs;kc)z***EQ78mkbsL znmws>rKh%#K2?>Wx(a`9l{2rJ-NLlqE&R38gTs4t*u;lfIHjy$TJGInW=FBm0@J0iz2~9tj)8pV7I$0 zJ31>^7j*Bsy_czLrr-frBp7Q8m$kQ)*H%|J&jy=E2?nRu~vfNQghEwu?8p3nbnJsu3^`%Sf!Y zM_4A)ouy3Xzf5lcyR_C4agZEK7xV36zFo|>i}`jj-!A6c#eBP%Zx{3JV!p{N6T9pc z2CqUAB-)?Xo#)ERLwzWGwf4BBbY3&QvX=fOEB}<#y|)V;zh+34PC`;8O`?EDF(0Ft z$0+78ig}D;9)rM79z#O))nh2GH{=lvDHbfPBV7bDas%Fkw_n8|su;zP3YL3&he zA{9lXqKH(KTZkf3QA8?=NJSB;s3lV5$rD^{{Yn5c(s>UmxDuuVN|QxDtJ!#4G> zO+9Q=58KqkHubPgJ#14COw`X(`w9X6qG95S?Il167$WIt5>)etU$& z*l|yoVwu;~H)5zpi=S9r^v7GP`?`b4wyMM9(XN!gp`f`cP8&y0sc)wEW(cl2$mb}Z z^6$NTN+Tu77Fb}M;PVil@;7mg%CQgeDRI0PINKxKMFnS5JozX*`6zGhQF!uEc=Azr z@=Sq@Gu&Ez5^~0uq*whc3`e9Q)Z0d(i{jjMY zHub}%e%RD+v8mr;Q$H`8&Vgz{XIAMrMWGDS>M(y zw>8UciF<99+nVLJX1T3dZflm?n&q};xvg1lYnI!Z<+f(ItyykMb(Iz+J8Rw6EVnhQ zW~_jVNTUt_x|%@~WSV|U;J^|%umlb)fdfn6z!EsH1P&~L154n*5;(8~4lIEKOW?o~ zIIsi`ELk|Pq;TLAPa}rUS;dd~i3wmPRaZ9&4BDwdc2F!iYi2nOu?uO>*4K|fdui9^ zNqALLH5ya+c|D;{chb|)+~lcmt0{h}rm3r{ zrQh!vNc4Bl4lY&H?@xUE1}AfCHQq}`Q^c@X%(UxCER zsE1Lf8f6D_?z@{{PO0Inn_-QbqnnN!%-TG&CyvXbd>#dP#u-1Nb=+0(hKCc3iS1H3$s4$|=uq(glQU5v|vV0#d34}$GsracI@ z2f_9r*d7GigJ63QY!8C%L9jj8Z=!GRL!@03Gyi~6eSSN@H{I#&kE18!t<=~JS#lU3eU3wP^|z|D*)AsFrmEkyFZD% z^vjw1mU3d(EzrIWXkQ1kuLIiG0qyI6_H{t}I-q?W(7q06Uk9|W1KQUC?dyQ{bwK+% zpncuaCD$#`mJVds5epV3+irQ%UkuP>eL}&0m9^K`-;kGY0;*{oWkPmMbk!glF}nSE zW;g1-Ugz*O#e>@?YP&DAr`keAD|w|se{8-!|0ma8^47K0^mhmA+pE0k>F$PsRAtQT zi%5hZ)j zwx=)QOSDuaLe+kE)EV|wrw4005^w#ZGSEi*9J>^f9sx;PdH+`&kLS@K>^fM=DhLFX zte(f!=oq7StI;&018OAsWrD9a9go9}lBs-*&uTuafkMezZq_}5JF!woY1A(5PLydU z%CwVZ3BT6uybf^00qckZ9C3gn4sZll!s$K2=QO;33&3YnO^p`#wTvj$!j)FI>PovZ zf(!v%78e14> zY)Nv`BqvRB(j+HMa?&IxO>)vCCrxtFBqx;)Qb|sl1Y43|OA>5R6W?Xc+vx&^nfQl@N*A%(gU9KfG0iRNe_6^1D^DNCq356yN9Xof%Rm0AgxrT z46`g0_UZSy5B0FmQV;uhSml`XJWBvWA+Bmaf9c$!E@Z;Id1SAH+YU4JjV^5;|9-hgXg%xbKKxL zZh(t7z{MNj;tdNIZ&Nn9NCJXK)hki~*Es;U)3DBUr6T0if~Yn+ zequYEb0)S6_gs6wQRMLy1`7=;i)cn zRa5lFxct`FEqrt;e7pfZKJ2*ce0kMzNi7uw8^=Ju^9l!#n>e_>`@!tp*_qbQ#$Z9& z7C+DDLDmKjvnWx05yXFT35dJI)Lz22yadEu0^%+KahI6vOF-NuAnpoS7R1>nF(K{}5O*nKTVBaP+>(ILQWnJJ_)&)Bh@mWqGnbV+G9@LOXiD`# z6bns{Slc5M?7F>iU(GM8WC@}z|6wO=pFsFxRmdMRX31vNqg&_3uWbOCw{dJ_6H z^el7}dI|a(^jFaLAhoMm$UuBR5$5)kn%fbFN2#(YF1unu!3t2Y0u-zO1uHs1%6rKA#QQ6`X%3)v1FS- z=HgR(cr#cMw2Y}dBjZbQj_lY(GMAEzi>1}c>QH;VKbUL}7Dfg-)#}u=pFX_SAI1e$ zy;I}J88z3C7)E`Q$+FU#R4C9850yB_qLckeS)#u6m)d$V4!t!q-eY>M^a67Uu^-`m zU(5T6Rcs>{E)fxY@oZjFqgqDVkM$sUCd$TD*t!pwOH z&Uy$lhu@+o9^yzT57I0t#QQnTamX~ACOCS6qbE3;^J5gv^Q#AyDI`64_o~q;{w5=3 z2wh>;uQ2OZK#41$#1&BD3Mg>}l(+&)TmdDnfD%_gi7TMQ6;R>|C~*apxMHD%eG(HT zu7DC(6iV#ncCTkp!ff?qJFs$4;-sJitvxe_P`iB5o@Ld{$rp23Uu^84ZAVJ(vF{Wc zmWvEn2ykyo!`yU8y3Uj~V5X4vDWrW0X`e#cr;zq3xNiz+pF-NFkoGC0eF|xxLfWS+ z17?aTn^FeMNp2Y*DhA9+&U;iFFh?<9j#>uHQNZgc;B}No6Vv6W`tJh&{}}Wn^l9i> z=qB_Mq>D}8bex7S&kAAMi|_h^g;E!|?+e`b1@8L-_kDr;zQBE7;Jz<#-xs*=3*7ew z?)w7weF2oZ07_i|r7lt!>@$qMfFnEtLEn=u)% z!tuQ!_T(5LCR91EZD+u|WteES75wDbAI`W2t+mdrjhD68*W;D56TIrdE}3;6Uu7a( zQr!u>qC5eKS~8N-A7liAO|qGl&us4Dvz<>VULf13qLKQv|7tJjv6tD{3wrDYJ@$eg zdqI!ApvPX&V=w5j7xdT*dh7)~_JSUJVa2_m$6gCP?30-2u^059vop$1Zh<13=89)C zf4B`6Iq?EX0gGf-qs#1u%wkhh&Hz!67Rcomv)*|Tm#^~U*P!o0N_o2&m>VtSydEmM zpi;fuh<2SznpTuISqsUQ(rybvb_3qKIanDHyOG1)$l-3}a5r+e8%f&@}dK_UKM;ONu z#&Lvk9AO+s7{?LDafERkVI1fA#(BPR>-om5=hF>#<@u(}=M&E~^-8w#NoUDua08$7 z3!8eIZxcS30A|{K7 z$s%I1h?p!QCX0y4B4VwIX_gvi;@G|!>f6|I_za=ww-f;b^{P;EKyO4P#9ho`EE~XjwzHxj+W*29p9a(&h9-rRC@njj)Cvrk1yrc0*Pb%>EW}CKu z8n1DO1Mam#bMtoXi`lUfm7T2>$)U6{yv3Go$J!S=o65tDHO9nSGnw68El$6RIPIfX z8RB#Zanez05kzNB36U(4Yck@rSBX<4xTM8NtRJy=Me7V}aXN%J9pV)pLYxjEPKOYu zLx|HM#OV;?bO>=egg6~SoDLyQhY+Vjh|?j&>5wH(_DM`}I)pe8cm!ICIN9CB+C*5| zwp!Va$Pt9CWo*{Cd=bymI_2_3d6vzZ9pP9GqY+u6T{{JUa{=HiDFy|+gaW|10B|k< zoC^Tw0>HTda4rCx3jpT=z_|cMD}d3=Hm5EIrt;DC-h|OAxgUwu1(Ud3NQqM$BR1Cc z`c-Ejn5w5A)zgpa=||iMz=DoKV!b^EJqdjpdKS6~y###?`YY&rkhy-Ph&wM+#A%ZG zi*vDiyc;Xa9sQK7TCJ6)n0=~NmgUg5wngtupt1{?-nleo?6+t<-Tebf(|<*p&N{B- zC05xsafvODa+*Pg9#NxCzQaMucYgQ)qXtWEhB=U0o=%STD<+We6_?aVlvt*6mW}0D z9_1_>%bBEdf`BAijVkyYtd@zLh+HQk*NMn=aA1)D$A0Ql$ol)*p>eEPl^%H!a&<^{{+`wgS;4(LG znH#vw4P531E^`Byxq-{vz-4aWGBKYa|4&H8?aAe-hk{oT~;?BMf|R1 zROk_r*CSiW`~f-QK$eI%$QKP+B0eBr3}k&_ZojHfRoq7S!o+ZJa+V{!W_jUyq;0|} z<=3i2JS8g13^uA`(xM+mg7`?kD!BTAq>ja;je9FJYF})i{fQ&@} z$lt`SYUcA0zg}eK<$MZXmJshHCU6PyUP8Q=5bq_Xa|!WYLcEs{?z8SyQUYk*ac}svM~*N2+e7a-^ypsVYaR%8{ya zq^caLGMAZ!3`EBYnp1z4Ko%`uS>Hx> zxRMo6C|l*0n5q5tErOKty3NvlS+e*0sS%HNyg6HX78kdPNBa236Y=aQq*!AjX$K~< z?X98~cMy3rH`abz#E}kVEJdn%!S+LVF&}YU-$hy5YseKqj!IranuwHRBo5+}YBa(~ zNUU`ej@n_C@WIY!Cc2=7yZ-s+L41;*M1jGfB3kMG5qCov!L?<0I3;W!zeVl00X z*PQ6M%go8UxtkG7@~$Cy*O0txNZvIh?;4VK4avKPBfqh%@ay-K3KDRB3r$n)y$}+dtIe;ak z(KNSh+1xKb*`KA0&Fv~3r;l7nP_Mjr?M{?>=2F&qLMjCRz@68n!&R->%uwp2cW^Dj zp1v;wyjE`BDKE|bwap_}R`WTl!#-)#O>0?~_7?(cA zrH^sxV_f6-G>0{QVkHO)`Oif_!o(UkYVCKkvmojtiK7`4PgUaLO z%T}6>ak;1@&GJug26fVJMF(+O#)o7Jss=%MDpWJM-Y=lG&s}t9(aZjgVA1$U4hrsR z8msWl4d(+e*&W+Ka#V1^>a z*F|vV;;6Dt@+!g6ab*dday;Vv`&FjiLec%}P?YFf@HEMH5+uGJMfbyFB7PjBxNSe& zHUXyzGEVU+|CaE~dC*eE5_=(kTjo>H@?qw11++|BXn7vAJP$9OhlS5`cjrON^PuH< z(DFQJc^FWt&RJTAmO0=k@6&=w$-u%+)UJzWB(@oe#VCaKLPwzU(0ibdK%as>5B(k%>N7|h=gv|&$qzyE%1B` zJl_J(x4`o)@O%qA-vZCK!1FEed<#6^0?)U=^DXdv3q0R~^?VD~^DXdv3+nlXnDgUY zOw{^m#c(?Z4Rb?1QwiDH+0xjtF3fwUP>~hyeLF5#q1?B{c<%%wtZ477hCc!a*5;6PsAK8(J+NIbI8t4snaUV+5VLP9}1FAsdMCb=Ck7fCFS9tYn0#f5@w+#_0Hq`zRHhZgT4!yNYrCVY7hKD@E>N& z9wyrk$GgYgOjb-g4ae)>z($~`0436Uw+*qQIa6gtXE0e3Pc>GE^1s9Ixm*QrN5&~> zHNK*xb=tAue03G?Vz#u(x@VQ>j(j!`MRyAXZY-^mpd}lnG&2#ookebS5>6kVGte@0 z4!Qw70X+>p13d@52)zQm3cU%La%-Q&l-pV4R;Trd+%9JH@I8^+G11gxdQI}rS8m6c z)q5_t((J~TTU3OGi8&Pd91497g+7NupF^R~q0r}0=yNFaITZRF3VjZRK8He|L!r+B z6LY}CoCOne7EI_myOLgTr*f-1soh0*>}PYrPVQWIGg}iIg|-@!hHrco0UkEy**d~Z z?to-XFYyaeU0tSwl~e~SsSfrJIuM`^l|bO81G0^omvSjjP)rhxR*=Rm)U^FA1Iqp2u%b+~%#8z-Eff?Lz zcss?FS;}X*I%Pe#u46JJlq+pb zq~VM-oRNkzRPM1vRi-V@&{@oS-K#Ih{*<{dh5MPd6}tAj$pI>_J4jU+>2EKMHdEAy zae1R0)o1+20#|d7FWuZw(G(l1?K{*{9UeR~oR}SMXp09LOZRwdn*#BUsC#+3buKv5 zK9Ou3bAG41raYc5C@jqTZytAQ{DIc?BYPS>jeEKW`>SgTMypdHcWvizbYUstF+Suf za?@B!{KFBZeYa!E$wt#8k7EZm>g^>7Y}A<@yO<3Jvo*>mL5zaWxm^gB`R*Ka19}2_ z8hQqL4tf!K1$q^F6EgAHK8cCXli)K=>(rV613kk4IbNk4CsK}!XkC^XQ?IkSTNvG! zpuu|{HY_U_wP=zpqaoON2zDNVorhrOA=r5cb{>MAhhXO+*m($c9)g{RVCNy&nX)y| zcOlar8nW1V2mzG>61PNq?J)cC%4Yd$<@L{p zDNtD2XXM5fU+BfUokK*P!o0ChAHDB(@;}d?W3% z1a(`8&&|hrk?n0&eZ?-(k-ej)<69E;R&{a9`br(yH9T4jGxBRLd&}6`4l|2*7=QNG zBbm@H>2~Qb{t$)=JD%amF$)W*YMQTtIK&!=RM_n}{g%z==Sg%;hd$W|)Aa`Z*nAXu zAi1G{--P&WKA+JH=;hdVIbCy2ZczpwU3}SpmDOY(XK`^h*<+}YdPJUXm$dS&)0Ouc zbVRtT+haWTfOGNDwY6P|WHc||wX35yluVWUrt|f+D?@Al!-yvjEFSAEudj<18T*D> zX2ynUjSq5-7)H_{wb-O9;jFQ?iZ&@wRY`PYwchBH3XT%pCLJ}knN-7PE4L+|Jugp4 zpN@??=E?zk5Wgu-DpePiWo1W8_knLS&@v?96E~nIpr@f{py!|$p;w?+p*JDZva(NN zik}^FEq9p{a%~HlS%A&{!ki1FPB3K`Cc0SI2A{gHZ^#_!l3VvUdQ*y;Hky5OrAzRA zhiPq|_0g{`l zyo2@8klUZi{=6wM6Ko9!DHA5VYWy7LdNMFvx zYuZlMRgqt{Suf94=}4&MW*Am#v9w7G+N8zOCM{@_7FezYZPJ1^X+fK`piNrPCM{@_ z7PLtV+N1?-(tirh+56bUec1VbtYXy_Fb%?;XW&lBSJ8G%OpT;Elt+z<7-`p{ zRELrnNQ2+e8;U?!$!C#ZC9!(C`AbjcFH$%|f-2-ULpm#Oiaa|>pqh}@NK<$XrZ{>E zhX-dzaml$3YOX&5b00xjAHhC40&^cx-Viz91Q$7JUPQ^k84f+ep=UVs42PaU4$dG4 zXOM$4$iW%p;0$tb201u`9GpQ8&L9V8kb^Up9M~r@<=_l*a7M{N5f?qH)CQu&6yM{HLo$bLj>BpWr3jV8G)dJbS-Oic zR{1i7>tClRXx`iHp;}WAw~t5MKH>ywa~gb(kg}$N1AGBncQ_yOcE=OLtu-eP*EH9h z9qrSCIlJ3Cn2ORq+}}3S73z1d3=aKLAW`az7uPg|+}>#Yn#UKfb{9lzYFol(T}vTP zb63z=J@2cIj+`8S-@hp-_?v2@+*xs0#CCT>>jSk@HSwyR!$WOn4s{1=JJyCO@{O;Y zsSd8)bnc4v?QiaxY4WUnt}@!_f4Mx|G&Wa1zejY;V$JRvkM#Q%IsUj)Vp<4{%IlLY z{Q_RL$gYG)QAm*y&O_E*TAm9qw6!&{*K{C?$z5Q@ExQpzZQBp>ISWbLauJ`}UnrhC z?e-PV)Bl2n_NUWMY^nsgjfqXkMz zy-SS_BiRu(ItrKRGEsf7?>27>AlX?&vkfkk^2kLOY*kY5e)eRbCk=uWl%onC4VPbz^T|C&bT>7GeqwFS9jWp- zU6FvQ%DNo#q&v4w%=aYv8e5kVFEodm zzbLQHGHE^OcvTp-6}Y*fOxiie3CDToH&;o9w~X4e>x|lWDZrsNwyYQcp%zALNnq%{ z3;DAQU2UDoH+9-55SW+E3S|_eKqKAqdQg6=YZi@j#JF|DI7f`5|Hpwkt^-ze@wr>^ zQ#Xtw=alq@&hm$|;Llm`=PdYh z7W_F2{+tDW&VoN@!Jo6>&sp&2EckQQ!XNu2CjOkoggC2Ah!*bQkvlM@_XxV|$-*Do zwl+-=v)-ga7xF2Uv8~fRnskzT6R)BP8Iq1DO~_CaGSq|&H6cSy$WRk9)PxK*Awx~b zP?MG9-o$e?@v_Bqr+O4`Q<9gZK=UpY8okHSZGOiVmJaS5A@SX;R9?uS3TlJ~pncF$ z=mPW@^d$6Y=vn9{^b#bA@10y>TA3jGU&fH+eJ%4THvCBo6;AR*CwZciJkd#>=p;{c zk|#RJ6P@IVPVz)2d7_g%(Mg`@B&cu_R5%GLoU~Bkq=gD6neLMc72X9q@5F}RX`Z|E zF4)k*Fg}|t=-Z|*}ZRM4D z&~eJSKie;J0XP&Pt2nuHQdILEvXTBNSpeMmMtk; zM#`3vvSp-f87W&v%9fF`Wu$BwDO*O$mXWe$q-+@}TSm&3k+Nl^Y}t~sWlPGInVDrJ zWygUiY1?|YJ|3$oMYpw~YrACc#_RLFXvb(QHrf%Lu8bso-c-bU>W=+D>!indw!XK% zvXcJ`HH6B__`iRAZ^koZIC}96-J}Y7+VL&vEkSOnQjx_-l4SBqk%s4~$1Dx5%J`eq zO4fxNqM{^sbC5I^R^Ip9n+nhJhhxwq(BsfYq0c~HfTV20m!Ypi--f;qDRa4qK{eC_ z<>BlphAUb#Y;G4TH`Pscrmb26+Z7;mPl87E5K;l68lLLt47zmXvj73vQny|^M;dz7 zDlFB|Z+5CJaQ))D*Av?3 zo_>sT`5n{7pBcZ)q0(+sw>Bu^dihXEJ3n*i@s{vCr3j^H+>K{qggk`k9vJ zf3H>jKPRf^M6YjgqGW#qW&id68>Q+LYj3HKAI(0Iq3)>MJdwVi{LForcc3rLW6MRO zImeC}a;f30lXGYr&FLH9+HT|hc-eV6X&BK1t8@V%U8?<>v@CLHL#`2ImLAO$;@xS4 z5gK8yMqV?_fr^)A5%`oizJye z*wHqJ1;QlU z&t!!yZF;>34XCIl16M^rft?g2*Nh>5*KY7S*g?MU-kKduSG3Iz_HMnfL)Z*X5(o4> z?-vrtTuzKv6$>Sdk%lp(W5R?KwoAhpX`~_zW29k>G>nmkG14$b8pcS&7-<+I4P&HX zj5LgqhB4AGh8Y-D$Y4EVn4wO6{Tgd^ahMGmBi~u<^SXTbZeLYVZA)ZhrWlXboop!% z*Lc~=3q>n-RX6w7-p&(*`#3v|5{0=g^7Tw*k+aii`b%|s8q?{-4^i;4Y%9v{tBacF zu0#)=N`C`#zW${z2{=o`r#wR%J_T)sxxV#NqPN?~d+DXu2oiThh_GvQ{sG(YF~^rz zDa|0DZgy`9VIqd$(GVtL2oo`+JX~H52r16<0%X*ndE>VGx8cT)7Qe;J;e>qngv~6w^oY(E`Z>Fk39riWDzB;pGy1>V?aC%( z3$=h(B{78syebLFFHo;4E*6V=RdHUGGz-#|D5?-)>!q3QYx|{{fdmMpWbziNmnJ*= zl8lcONE%WQYJmEoz0eWpJoFyuBhaUy&qKcn{Q>k<=r!oO5LS$Y2Uc2^rtX`j0!Opn zQKnR)Wr83Uo{06HZsf#^I6rK@rytv}T&ZR>=M%iWQOADc{Sxz3fRF2TN9)+#I9i)H zk%|wIiLhgt`WVkZ;+5^>5UI}0dYOr!>OSV;sQEL!1v8hi;c~p3IeQX0E=xO{?-p*m zeQNE(4iKE8U6(0yCqQb=>nfTlCX{_6J)sdMG{S^Nn9v9l8eu{sOlX7&jWD4RCN#o? zMwrkD6B=PcBTQ(735{428nGrc0vkru(xHiwSY(o$E6=Mhz7r?jh6Tv8WyqM{#!0tP zW%1v#_-CiZF^xyEDu=I^MCa7M@KP2pg)CkQ!Al`{DFiQt;H40}6oQvR@KOj~3c*Vu zcqs%gh2W(SycB|$Lhw?^;-!$qOB(%UFX>KsNy3Hu13qj0Vn!ll*R$V-ohsv@;_7^( z%wJv7Iv&fZQoju^wT%_z=NIJ%YRdCRrZ*@=arZMRvdL$f`Bb0aAL3{ngz(o&Ou8LZ(8t(}Pa47rL|_3ZCdD$O$C+#- zI#hSsTi{!R*{8M@17RV9VmWGMN|tj}PY;8j3iEZ899m>Da}$wF(XGYBS$eKM=>Ku zF(XGYBS+EEqnMG@s^h$soNWf<>wu6f61P*AJN0{Rp;nDIw)1Xoqj=Z0@noWy+m!|Q zIu>A+<2P2tpc7-CD5@$xCA!A4R7_vS7R3^5d+(mxcyfDgt8>`x)}GkKIab~*M)C#* z-v9R{{SGkgURT(O6)f){z&p6<2*6CGYQZIKh$Rf}7BCrD_g+kw&KB>*3}QgC?!$Wt zvFl&Ojb^Oi^)bQf-Q?JB`^=Y<6U=K|wq6j{+v{vPOc)>DtZz~0-C1w?XVOB$8jWQ} z6O4Lz7;U8O$-tYrNxa27zZ0u%RpNEBthG@!&+mY>CKuM$+DOJ!lyD&NJCEb|nZTlx zWA4(1%WFU|?$mUvo2&ouyA*r8;cM&c4A@1X27h!e;Yro`M>@p$k<;#_%UFPS@&sT| zup(R&j%mr~vBXPzLweOJpbnA4h{+N;r87h5%#fusL+H#9Ix~dMxLZNn{P`gm5T`1HpR7n>Kl^$OxR0+41;wxi9F!wZ4C}mkT0j`~@ zQ$xT%2jI5Xq-)EYW8ijb)cHBI$lKpgn)GXE(vag>Ta)t8(rrljTXPnd_g!U5(KFFu zgfC^kYdb}%zd;;J!5sbhtIJQtpYfmGK!uK+J^T63)AH_5)bmRyWX#cGRN|cFvHi@h zpHJ_fVB%iEr1(kbW6)>iAWi^{YDd zs|Iy!TSj2Fc*Co(sSXjpFK#F)?5)dYMfq$NpWW?-O7%YPsm48&rAVDz)r zV1s%1xIV->0_lDGPd78}{PNOa0L!=w%JLw%RmRJG&DHkrj)Cvi6{?TQ#=2g%IxZ*d>h@HxZXu$7Mc zh@1+zQ%Cda@71bsqh9wCW7DHzYydufO6m+hwL~I}XoL|BowsA{)Gy$xIh47e{Otik z4s{vTBb&2%PFQ)R%xGm+q@5W#YkT`!<%m|P0dNa%!HDuSO0({`=?lq)pu840(CGmF7<6hG`R0^jeai*W1mTiE7q%`ujFaqWGyCU6k=R zf7^11)oine?sRPER<*nsVbl5gkN;O@=UlP9@k!XeoCy@hpEdsPRl-sVM3=+%6tj?J zvS#}p*j@tAd$6o}h-Jk?3foWdDe+;fq~TqxHYwO!z&Qd?3hIMqAc;?HV+Mx3;q8(OYDKer51gyQAz}jp(@hJVghE6_5_>|2o&_!SL-t|5x$S`zPHpN{t@`vqstEV>S z-oLw@d)LKO-8swZdM&aUB@WC^PdzvQMd<>^|3ncjGTgGx3?-zJpBcK_2%Y6MK+}J*c=re7Xmf-Wr7)Na;YujH8Yw z%jEGE>#q*X1s-VI|258zbSip0tfJ70ld(%>EU4*-S^zH@0@JZJ3qd zG%}e8>jctFK~QCMPAW_tg!LAj|9+Jg6&9+FBa&1nf=?P)BN2$jNXo5??_Vj|ab!!k zSL&112qZ|mpC64Y*^>BTu?cnUQjk{mxV-r5yq3RCu+9rsdyzJ^)|Vq%If6(p9qiW1 z5jym1P;P3d9ZvQ*_=l<=aJ2MWOE+b zoJTh2kgjz*|bk$%H}*TXkN*t)M~eDujQ!i4lyh{wz5U-7&~!k?@?$k z4uF7m$lbZ&X*%QrJOnl-rnc0!iXk^Tql}$1+2Lx&AtxJPYKN;CPe(JxcQX#TW{|BJ zF=)mi*Nj818HZdm4!LF=a?LFNn{miBD~Ftf{dKAHiUlhgYQ~bz8KiRtPMJYEXOK=w zES1c(8KiRt>6}42XOPYrq;m%8oIyHgkj@#TQ&p;zn3EYxI(5xUkBYbd#503D`5k5G= z2S@ne2p=5bvuqZh#SuOv+K2p!kBl9v%|bSZjlLeUA{n*&Kk>ygpD%9hjT8}`=`5@$ zt07{uG~Uoq9!-bJj8t_^Q@KQI{@Y*tNVL7bs<^t^U*M{&DCrrTm`xQ`);0%sg`%PS zp89zDkJklib{NyLQqJcE3n~(~#dFHmgsgFQK4B8&r6)|b<0P1?L2b<4;?=gR5hY4E zV=+@!%q)qw&~goBPo*4lFQ)R|2b9@UnF+G=5!h~4n%iVD^YV(k6@@j0MnzS)G}64m zO!|A>MU~}6dHK6sd4X_wer0V_Rqj+$-1h~?nDMOaoRLruNlMBnFI%ia`X1QQ*N0B( zvvg7)I;jt>)rZ#VLnrZD_#V}$CUaD2BFd?7o>Dd~Dv1sd-K3j*MEN!Mwf$_WHNf#4?)`~-rZK=2a?egeTyAovLcKY`#U5c~v!pFr@{VoFp&!V-MFEYpH7 zq1ux7K#IE59PHa63bgL&Y4U|@tIH~CN+aE|ii+(a;ir42n=0x(zRKcgeAmvvL5E4a z07`vm-PTSfU&8i5-SZv34R#W8JmSt5ZZCGtJD0s!0v%pp3ZK$Xu|CW}b^~Bpr7!Gl z%=`B|>s_w7yLj?YIUx$p2K`_xK3{N*8pHP}JYm!fO32Lz^FteMn4(k6UIDi#J71Rg~K>MJh&;{r*=t=0)(6i7@=p`sOI3NRf z>j7e?tAt!2G5+7}mIa$Y&0C+}IyD$53XvYymPwDx10xDl&Q_kv1;7vC3i^}1Nu%wX z-(35S;WchNc6#uAeNXetCKARzO72F!qfT-+=njqECwC(srgta{R4NNnVva0wbk4IQ*HLXfb(xvev=Q%i`6RS>>L;)K|Vt-XG6T{gH{`WhPf#w zJpr{_Se6{7&M)Rv#pwKEbbc{9zZjigjLt7c=NF^%i_!VT==@@Iela?~7@c2?&M!vi z7hA@Dv8D5i0iR;!psdPVwAvi01warVjO@T!K?x%UX@Qooi8^1AS3PlsA41uS$fw>-jmqjQfSqy zW^LNxRn~hE#}6`6ZjKHN#tzG1Twj1%3`QyRlI9F)=3dUWPho=ehRRVCIVF)yGr~3w zb5zNo7>9#m9GaTi4DD54NO}zsRoXJ_g`;|T(G!w9)r%bVB8R=mVJ~vniyZbMhrP&Q zFLKz69QGoIl8)4i9QKkt)l2eJFUeEAdJYR2NU<=3-T7Ygt&qZ|%-<9jzQ-92+!rr% zTmmV~dPYpWR0RBQYr@&yRXQH4R~Jfjb(Flx~;r*HE~UR~qof>f9^F8`B*% z?y8tKn(*!@`Oi2=40}cIz}oK^Z)i?*{vMo23hXLwAzc}4mC49g=Dh@`=oP1wjSz`d zoYoGfNhI5ZQ2cGPKCwt(@9(b|`p6r1q zd*I0)c(Mnc?13kH;K?3%(rlGc$iU)Bi4+x6ip+4ulhSzp9`mH$ywjJH1mCf(xO1}< z!yRyEsNC(ZF3wIx-4SaRxvFa`3UcyNb6IoOQA%T7b^he4W3nW?W`~xJ>QClr1w0Wa~0#NSGtN z=5Dk~ZLLRGUE=?iBh8KI3m?e*>^P@=@MQ=tmqqxGZjQV;tMDA;HG>FjD9&;I?h`29 zmj%UU0W4b%9+o2>&N|}V^2NLL(!QIey?EYrINB*oL{1^%r!3e$g@~L&L{1?hr?}QB zMC24AataYSg@~L&L{1?hrx1}-h{!2K@bsMLctBYE6#0ikW^-Ldv{=Ds*>5{dUXB9lnO5n|s zuChoZRNC8D;*Lc_C2f6?q5@}ez*mt>)|LIlkDlJT>{RDuT~$|SyRWV-Qfj4xub08< zNO^v5qBc>vE73b1NgVGg4pn=~!s&=Hvi0uH^bhN!HjJ%@^vv23FwpEUx{c*IdtZP6eE%P=^_4Bqx|a; z{$=oOlc?bkI#~%&j@;(NuG+p-^Ctn3+d!5YwVUkRmsoCj%#aFYkOCX5;nzoU>;BPF zSGC(CozJK&;xRsuy;u#_1>L*6qwx}7@ybfP#vKm0*TpMW)Wj=0Tl1VxRXKlQ?TejF z<>AH}oLvT+aOaHU#^(r{DB&GCd4~mXbh3dJF@XDeXCHTFDKh?_r5|+hDkD;rPA?B$oZyYw%6`K9)M4e}|bx(|||H_`{RB{zU)cmU7v0G{CiJi`Nc zh6nHr58xRdz%x96XLtb5@Bp6S0X)M4+%Hf43WHZ6vE(GoY(UMyeUK|^+9JHvlxFu| z63mufreC^{HWN*E;&u|ZW2-PzRBQ{$bZ%S1^iE`VTbobGHr44~Co81ixJ6c{X%-Cd zI__P4x?4yZeT9_#^|Q&N>(@!my1|Up^@xDRKUwp46z81l5wDT9VhD1l5wDS`t)Cf@(=n zEeWb6LA4~PmIT$37OEvdHOh^GYK>efX`WZI>`WJ1D`zIc73zofLPwzU(0ibdK%as> z5B(zN2BzkQTov+{b-ba9Eklm5c_c;_FE3bey+%>#}3}Y0G+(1F7trl{{i)b?)2^KXA%l)tb8&P?1@GLcOWp7g|rcdxM*>JrRpi7#ug^9SUL3ccn zG|nAEpK;M2Z>tV8HKZ!z{`|5%-30K*eV%Y(MgebM^3!d#mkInwAA z1>rTFOq@TV?ehfoIeA{%K2LzA3D7hFn##6D0yIs4rU}qA0h%U2(*$UmU;+}LX+qiO zMZ9?3%*)ID-t#TVrp)DpRqk=`Fyyl<>I{mY1>J^A0(^3fVkPequHicc+dyvCsA<1`X!R%S6I9gYE%WXet^+U$G?Ivv^#u45}zjUP7uAQy%Q4v_yktk z1j>%z!Duaf{ws^o?EIuY(voH%VKgye4!kVY=Vbgmdwha>7r2>u>xWn2(5rChRqpF5 zKfKBhx%xM_rN4$0Hxx0bhMJ&3XcjsKJpw%reH8i(^abd7=*tj`c7fF=IsKEI{z*>% zB&UB;-PZ*7^|Rd9&vIWf{y5{0GnVj^f4jM0lnLCHWqD9c{I&Pno8$-@=4B$CY{xLH zE*?aCzx@`s_aT*R)x@ij^r#8RosMB|$FR3!*s?L~?HKlU40}6G%*A9^%51)?wyONeSy2 z4xrSsj>4Kj5N8m?83b_#L7YJlXPCno1X14S41zd=AkH9&GYH}g*7yw8_zc##nMhU0 zKpFsxE@U?T;c$iIKU-jhy4JTIKU+iaESw4;sBR8z$FfFi342X0JE!2 zk>gC<0nU1wz4;5s{sA74+u)l^{N)l;`r~~5IG++{@zb(juc3c*m)f+qdd#_^EKJQp z>)D&~`XIeE?{>O2&=g8Iv4 zAn6}$2)3nym(SE7Gy50{W-^8d4UF7R=c zWx{{YnM^X1%OrEZB$G@s$xJ5qdzz$8n_kijrKK&sLCc*2ZWTl=RH|Y{ZYrYeBH*s> z>WZMauC8Kf#mla;u>0+bx*#ZitF8i~i@Po&s5IaIdEYZLX_K~TrR=9a+TU~LA+Zx;YJLh$|rgILvDqQUe)l@bH9o}JoIgyE1Su55i{wvnm9BE57wOgfL z>&0+OUozB?ZU{wUfx622f!1Z~JuQQ2f8SWklK%RR{$M&$?Q2=s*WBOWv+c@8UohJk zZtm{%);Au?fGQ&uu8MFl?4{9)VKB_>+D$ubC!monxNo|7lB=`&) z4&vq9IFJKIfR%up(!w3Yb}C{zP9(5E97(b-^%Pdina;PST+M#9jVF;(b((Kn0N=O( z+PMI}aRKOif#w@4`T1)K{9#6c(iySSGrBu@ggD}u ziA|in2{y5b-*19VY=TW}g6ucJCN{w)Ho+z~!6r7rCN{w)HffPyo1D!(5dB%Di}5y) zwqCGlYc1r7`j1caP+?H)1&E2`_Cl-P1x=TYTf^?z1w_}nLY+W2Dhn5eS$2 zlcB1r3Qv8mxv8us9;n?hWSz0Taj}Svt4Fd!B2L!!w6|1W7;XrcTmA;$OApQvCjTxs z6pGYVmnPf$B9Z!lCl(6@ed%m-SGaS@`KuSNtMvLk*WD1Cw{@Z&2{Yb4nu>SKPlryw zV#|0f#e^&36|X&t&}lydNiI`o*=HVLr7x9b-O*lfT(Fns7?D=&`Pu7YPm7(07ByVS zDMsw9)e3_?r3;em0(-h3$u3Be)I>;9ssS6rmFPl4%(mpX$^HoYq8F{y^1*ueL_se) z141+nLM7*I5)i?{&yUV25aNu|!7~)43L&$KX)E^(_xyZ0W4^Jq!F?coA;eZ$J~e_$ zT~#el${O9y8r{ws-Od^#d=od?uwf#vPOXj-ZS^bvX5;dWIr%A-jng1ZKT>DADvUXsVbSu^m;NE4kV3QCF!y zbwv)So1vX;nu?@->pS(HEPF-_znndZb(0!|#rCb+PNdFAj@uOKjEqAz4%t7Qb@NS}bvYN<&)bxqK<_9RV`H5HJY{;fA=-tp&oJL8s>m6`nhdPG^L;?vyijPCFwb2Qt%^ z)EOf8W;tWCA#J2x&F91enNd4=SQCwdfyq!^1yom&r@9KLt^%s7fa)rsx(cYS0;;Qk z>MD5M3aG9Es*^yM3aG9Es;hwNDxkWGJk?cbs*^Um#)I@O#`G2I&ngO=aU3sk_JrhR zn$6?Ll$vpnDa}9P9v*QI(|#_qwb!h~ds&J1=2zmqjLTleWiR8hmvPz4xa?(I_A)Me z8JE3`%U;H1FXOV8aoMX^;_I2&y^Q!=3Y&-!Ugp%`pBu9{bg`xvnUq1Xj<8y9KIiD6 zvwh2&0zEw))rrN4xiFR)>$%Ggv^3Uv_8csq$#RZSIAz7UU{hVBwAMd2K6EJm@mH$$8y|k6S2F_Dl`*&+@G;{4Q#^u62N)ZksKn55BCV}IS z0!(b|OesK{9gZ52Yy3vU3d3$TBA4VNZwZyqv@C+b`C#w)xZ#RXxFTD;L9?hm7z_%s z#WlRcHN3+$yu&rT!!?>kt>qFo7Fg7=Ws4ocUZzLr9*?l$SOtm^x(CoqjP&tx$Dg(q z;!j@@S!M@gz6q`>0>=(5fxo=bz$kJ|lmyLQ45xZ6pReWfGCnWkQ^c8FhI8$LbM4A= zu3d1hU2v{laIRf&u3d1hU2v{laIRf&u3d1hU2v{laIRf&u3egQ?J2|^Z{oUdn`TBh z5wT{ju^2~CHBMOkalPqSvAe(f*O~0<#3iI{M^8L0ot;66J^W$U`w^f>7;%*yz@S-5 zeJ~M`#4>P+-s?dKUob;#T6g?n6C7uhE3E>wz01o^9LND9z)D~%a0##*xB<8wxEr_+ zcnEkL_$lxrpi8NF*@*)=Kw54tfE0#x&f5}>4Cf`Jm9U?cu%DH%pOvs55x^)a3x=Hu z7M}?RJDbx@*2_vRalYnYJ9!o{U1%=+MlSJ2F7ZY#@kTE3#)6Rb<^l(sA!Kb5hJ|um zFjCGCvS!Fs!}62iX+qX&Ik4KuRHPH=)Knu=NjLacT&B8|0egjHDr^hP&QLOMi(6C;N8i%+hw48(?BTMzE*<4-r)-$V{>H?OR zmhtt`Wb4FO=+JKVwH?{!pue%!Z@J4ujkR@&j%Z6y{FLyjOwpq;FFV{4cIQSTp%-Ry zl;@tV$#wNai)|T3uN>|zwq*#nFYBqOt8c6+E3s=Mfn;+>?GWMR_veNxDk9SyYO%JF zM)$mI|4}6!J4J{O9$GbGegCBRXeGXhE_!+bM?e7CGe(WE0tF5Uu?!t8O6*lfE_BiX zopeAa9neV!bkYHxbU-H^XjL6(RUK$m9eJ&)Lu*x1rcW{|3wDONyL6=;<=ox68Hq7F zPS>t|moyX6-d&Io@2;9>J#q5=yZb>k;{x6fBcejS#z^t`jutR-P%v`vXc+mb6BwJ^ z92&FA7zcE10S5<3BfqGx|tJgRV8d(?f z&K;4KUROZtO9ZVinTFP{dSvF5XZ^q9k&$`ys7o~aKAFB5A+wX?pGiZLC(B1upkCZG z&x)HyohQE2KPX4deiD9-FK53x4;z#5{)mm{f&$H+Ei`-fG@6~EuN;p(rD#5R)p_GV zL5l&cz&v0Dum!jnxDvP?xDB`q*atiah(?lk-pHIqw5PTpUOBGNm&J`EWO>52jG3++ zBcUB?4*k8|IYNXcxN4UsLM*20-7fk(%++Q(v#P=Wru9_YSC6Bffw8Kdpe1#`y3sMV z?pN=I_>F;9tgAN_i2nv5{u_=){I6P9XY~~SaamWD+NG5gV_g+=iKrW+i*%Tvr^^*7|>DSVkJOh=0N??1b#CuW~r>>{Sl8nuwLvM69ePVr4ZED<@C$bC;H*z&4}_o=J!NbbSeA*1|C zdZa`UJLz7jHsMUKjAyE7*-6=tY3I~@$wBYMyZtgJ5bno^9n(p^`-yEeZYtxP8W)-! zl|YGE%i6r^k2-OGlr$if;STCnqy^K-ad@UoKkK=7vT_2}^%KDjZgRSiw_(>0F zRGd(!+Evq$aRq2e^$`>My_mR$#l$r%2C)_su&`K1eP#Gup;@mKStI?GgrXW4hFbs} zlR&R3_DvdWG5yv2N*hFGOMewdmcd?$6qo31xv*SpZDGZkVJs|SuGGvfp^a=lYxvdV z*tz&r3C3rDmoZxl`8=f<>NI!!bxmt=HN8jp+!6Znd`IwN^YG)uZF^xvcOTDRsNQ&D z?dQmmY$xVojeX$(aHEtUeu*qv${t}X>{TG-FE{93%Har*0fvA{K!O}yoVrw_yJ;It zvR|eVhg$0LN2zDi!J10s94|ScFzD#rsHxbY&jGr4Ih_7 zVasuPEl2QLj$E}IQ`vIDkCqdDw4Csx<%AzCM^PjAn7KOb$V5>pavAwmg@~USrJtJQ zj3y_*hl}-@_?AB6Tl(_xEho|!K}JidK0R8mK&7Ys_`BaH1H9!46VbG&YKQR79Q3aglRGee=@Pm7g;?&-kpiQ zVMj^0J{y)q!K$wD6`9`6vG9uiY1!yQQGYQR{m08UBw~qdqRM)4qOWI3MY6AT@~n#1 zzGQ_-`s*3#A37{QeJB)|mYsfg#hQ^!W9!0G3720wwa_v>8ZJG+ta`{;U{$CEuUufZ z3(R(b*)A~KMc9&y@Fo}PvWpIfE;<~#=y2$w!=Z}~hc07Wc!Zsw0QPnz)pn|NF05p_ zm+G)lFGnO{wWoL3XPG2Q^7Wm1$wR}RkHzX!p=4Ew#~-N=Mw{bRwF!4cXFSnX>u>Oe zgH?grRCRnF&1)qD;}wVllEuoz0nIBcTe4UcB(!RbQQ%T_W(7+oF>^WkXH)zcg^($m7i zLNOySZB&+l(W5EwMfvJg3?&-vHee2sY6!4!f_YEuQ}bCE7Mze&g%&Jj(dLe;b*QwT z*9N!aD@lTWdcRaitAHs#W&fO1u@PzRQ_3Nl`#372S1<(@T@Wc8UPkGI1Bl%vu-%gO zp5Fd6jeyCZ9e!%(Wv*b?(0H@`ngO@JKEd}9rR?i5$+?j1ms-C)0GgJOs3uYvjTER= z!tx^F**%13)4N8>5qQ~&136#>kj@HQflGkhzzx9dz}>)oz(c^}z)yh}0Vh1Wco7!~ zk+po$swI?KDd_oK4m<-m0@=)T*R6bc39uWu0k|Ew8@La62oQm6-V-8Dd8iFV#yXOq z#fw#O50QszHLH@Qn)$tQDPN+$ZFi2DEa=Gsr8>P(XB%#3LTY3}YNmx~$K*iFglNlz zgc(n%Y1paP*&5ba>dBd!6J>}^Wr)IMaE~%X$}-aB%E+H7BcHX5eAY6&17&yz%J2@9 z;TnWwb6GTzzN*3p-2ZmJDbM7=esNL6jbH?OmM@nFw4t#$EW)WO_68gt1Gv|0q= z%c!R!m@DbQalPk9L?Wf1F{kXf4t>$s90!}@d2Eie8wZ=?V6(Iyh=a{>kSh*y#X+t( z$Q1{<;viQXDM6lV#exn|9iL`SSpFnHfCMIc2^ zZinTWWbYw9CxK1C4qz8>EpRJvCvY$D0HB?4kFfI-KuQlwUQ>5rz8s!g1%q!c>mJRF1+_&iYu+`dH53m!mM1qcD}DFqNY)=`L#xzt=^oQMAO)l;9~T>2IEr zo!9fYUO8f(QW}J0c*+b>SeAAdelYbQd{Xei;U?@h;sI+B&uk*Cs$t2|Ya zrq;kfmI5qU>zh`{mQ}6KUL_&u9yq?Rt18IAlo*o=K!=nVlcl!>W<_;Sv-HlaH@1~8 zF9CJ~HvqQ-cLT!f9|9f+ehRz@I7@HwA~doWqL^8tY!k0BHgsO#q|`fHVP+CIHd|K$<`vX-@nig4(8pnS<84T7;lAJLU=7dBS#{ zu$?Dt=Ly?+!gijpohNMP3EO$XqlT~xTA%nus-^WgNXwcnRrDms$Z3;J+7U-#?1$Aq zi+rH5)ms^Gm6z1iSY#JP!nuY}d7`~LRBP2#lzTkkDu1-PEM-5FZ3=lR14)mk+LP>W z30KwkHb)1$8h$aa$L(>~_$sPwzc1R{9_(t<#f5H1vD%IZHKsmt00~BvDZ%X~a9im; zsjWa!L6%}!sZ4>m3V6AxU@0{TOxZKz&#Z@~gfvT$q6XdkvQSoG7!EG^KEr67!*KB7 zLcZMsjel@{>9L(79oog+qTH=g%XQt(UQaJC!lH*nJlG$ucO6=Vuq>RNJt^zKhWTAd zf7`PDfn;0KUs5PUxN=F=j3}au&T}P)H_mT6Yu&t(3ok0IYaK`y$`M|6t?z3K>*utRI{t{d5UvYs$`wapWjQ%c7=vfQnxhHP>IYv)B5>xO3BwoS@ znyvwhI5x?#6S0&y1b%3(+PUkaUp`W;+T*qDZoiX z$E4=0A8N1B9e$iPl?_DFH;9fWZ7Lg(nH!+m2E_0N-L#>BVe&efn_qdgJT0Y>rKGHv z&z3y7YyasCDa#R)D+=1NQ$|1+1{9$xD5{s#+6!{qpjlx(?8JapU>>jn*aBP(TnStc z+y>kQ>;oPI9s`~RUI18O#Vi|SEfbxiME8M(A#uAzUeim!lM-lp30PMGO)r6_mq61? zpy?&h^b%-#2{gR~nqC4;FM+0)K+{X0=_Sk^%GhxXuRH|&zWQa0bw*>o}((^kLXRT@M4qW(&NV?LO_Qx}|zF>W`9X~Y`uV1iz(OYhe zM-Tog?h2mE=f4GNQp2m-2A1Xg@Ox(3b}`c(Gy{0?043JltYX6N2jTZds;TA}^GHd{ zHs(q(XQG#Q`Sik_EzXx2W&Brh+A;7T-(moPx0G8%fDAAMOahyL9l$Q&THsdTPT*eP z0pL;KN#J=vi$JC9M1Tx{2;}Btkn|4edTSoD*j-=-TD6O4-mZ#h6_NuXqx1_}HRsVv zsy#O6(W)7=Y6h*EL91rasu{Fu2CbSwt7g!u8MJB!t(rlrX3(k`v}y*ennA1PJX$p~ z>7xAzI}zk+*6W(cBc_+Ate7XcX#9SBQRa3cM>}XnDs#A0*L5=q&qD)+a*g0#P;5`y zeQ--`ERh~*izWtQy+f`@b5C-pGo0y9T2=9um_P3KS{Ht!GEiOaYVPve@h$04%i^sA zW##U)%in$aqRdb>mP;oq%PPxSm-f{K8{1C&VN@bhpSJ!U#WSKd z(Y+e>69A$P3X1j_V34p2pg@Xn7twp7g2(dg#Lr2Zr8r41sCLrHOX=D{2oHWEJV+9E znBKq;*3bLb4ku&1s|VWGFUqWJXjx}nTG~CisUy33tT{e>#>7jXvff_dKKNc9F9VOP zz-$pzZ<2P~9^@a#gCnK+-Jql!ymo`vZt&U-Uc13-H+by^uifCa8@zUd*Y3O!?nVfw z+4puZM?bc+Xcsk7=RH7KLWX=UX_Fl-%fJYj7t6|wWkZ$eiJ{0PI}!tH`t5Ir&fWWg zRh_%ve)Ul7EX#GvsUN)VHMKmsYpPT|t)KiY2Y6}^_teJ0@3QlZegJd{_%#gG4dzRO zV(B62#+>3tLvf>_xY1DDXee$p6gL`*8x2KXLuwVErx(dEYd;{ZCdxnt;apz!DEq?ReD>&y zz+N|dEY#Y3Sh=N1ueK~G)beWSeWD!|b&hnYXq3SxxMmB3cu5@0uQ18_TVH*g>D5b!wgQ{Y9w z(J+e_(f1o=XjTEvEhf2zsc$>NrWef z%d3)|js4>_sg7n>RU%Ryt?`uCges_=S#I6b*v|KLU3~9v=;Zr&q&nt&p9pyEXM5^8 zN9vRFdm7emO3!bPf83YL)`imDwbZ<=47c?D?C4)J;F+o4s!vikxh;0c?i=wnG5hA%N|$%Ep~E?o!nw4x7f)oc5(|TbhMLO?Bo_Zxy4Ry zv6EZu*ef~Ax%@|Y=InS1U@<5>!Q)#A=sj->%=g!#NH>aY$FqeS!Ba3 zvSAk4FpF%MMK;VL8)lIWv&e>7WWy}7VHVjii)&e~DMC&ui87u&8>&vpnp?fL350umacuTnt|ntwWPSL;;7GcDL2pC2Yej!-$hXOtjN$iA zMp9jMRo&KeFSW<5?nGm3c=N)p3)T;X>$?uF_a)MyUscE3Cr(XoTE$F9r+#Z)Z+{Vm zeZ;QHF*8;Z!cLMhU4*-tOCyu&_ z^Sq#L7VFX-;2xF^Nu+r?y`V}jsL~6n^nxnAph_>O z(hI8ef-1eBO0T)+qwG8hh&*P6d?jURZDz>Z7$hoT`h3;f)-;*Qy1urxCzrIlTpdFl z!TL-v&={|2?eL`0-Ru0pwxxYN%X^{+f1a1^?55^<-R+%&P1RMoiA9s$^=^CaRi=s%E0}?70y{s*&aN{7S4$9Q|8nMC;$h zNQ4m%t^~FMmjJtg8-Uw^yMg;gtf)xP>^$P%c}gbqWNWxawd zRw$1w+h^iRMYw(juGsD(uH@`qt51vbWh^$1oKtjP-k7U>Tri6G6K+^bFcf< zSCy8BLm_W^qsW8m2 znYC$%PpA247%F73k`#hf{QL~SVY$r=oU#9o$aS4Tt1Aqe8NS}b2Tj}WoM@1u{mvmr z4lw!J0c6aq5~rmRyCgd6(NE1ba*xk;(9Ruk)Q&oyX~y#!XFNYzM6@FEX)Kyud(RKA z8E6OPN*S#PkO78(N#IB*C+!3^${AF1D90_li)FLm_Gy#@jZUD^tjL(1_2}rbw z?=vSddT2A&v+7fN^tKdn@x(9?Mk0iF82%gyU1mj$qu>CmIy0?4sh@T8A+tMoh9WOU zlNpgeWy18M7AH##A+s@x^lYp+1RWM z>&U!`hlBmATQvKRF%Z&aQx8OO0WkbscsqQZA9N{YtwJG)TkZp_x^5(1QEH4w}EcbT;xN6r~jep_vhoP5Y3heMr+j zq-h_w^&w6B5FNynMAHl{wn&Nhlk8Mwbi(8G3DZlu1O};d>v@j|W9a)WP#?4^kX0UNH*ti*N+zd8uMz`CH zZnqiTZgXC@+pKjvFOR=jqiBZPCG(nV5`st~#U_ykl1Q;hewRdwO(Ml6kz$ibu}P%Z zBvNb=DK?1|n?#CDBE=@n9m?2o9PvqpB&jvmC579`+pW*P-Fn_`eg5s%^LFcbyY;-? zdfsk5Z?~SeThH6A=k3<>cI$b&^}OAB-flf_x1P6K&)cofzukKMcFVZkdZW3DZ;aNd zMR}G$?y~5##(GDnQ0+?3N>$bmPpA&{rQe^O8c(R^w3V2Zh&5xhuY#O=tY=U#VWcj} z99V_j;0(Z#!z2>h#kji=U@;hNHxVVA4^gBZmY&KIH<8k@2>X$M^Lu`0Pn1|UvS1S= zl`ecM*E8`b5{J@PATCGLD6-*Jp@Y_Gq~lPVc2a24aUy;70dazs8ckW+&q`u!DX3Wr zYL8fqOOJp6n4cWB7dpevjYJ z@cRfj7~%0E;9vwCjDUj?a4-T6O3Tg&I2Zv3Bj8{J9E@mHQLbB?$LU(emNX61rm~jn z)p9*jDu5Nh7T{vwO5l3nHsCH`AMhaX81OXk0^nF<@VsG-k#5NmO|eo*q)op@x7hA< zSKvJVZw#hQo%O;x&<`GuSuPBs{e3pwe`Eegb4`6);^1!{yRo!BR8#G(tdgbSf^fQh ztm}nGOVh0_p6o`u#_J0-B&2h^^(9ZaKUG;>+31(m;`I8anv#R-ZQF)@6YeEg|eJA?z(7>@6Xj5+Not#A2XT<}&8BcoAnY2*Kz>GX7!*sK*X~Y0TIG>cMQ<^L+cao&z{U6Zjp!skk0 zD{u+08@K_u9k?5~4|oW89QY~lBA^#FFFSD{2Z)thcra5_cn5DZkGmV$0Ch~Tq_HG;tT%CZ<<`AZ^U+bt{Hw`FlAvv^Bi@0P{c z?BXp4f1Ms%+u5;pEX`*}C!cRQuY0f|v9M))vblM2B%6sZTHCs8`{2;_Wv$s|+lSj*Ng|X)!NVwc7zGcb;9(RzjDm+z@GuGeQSdNo?o`T71jqmm9!9~#C@;e#N=k~cJCw2~3CcaaF2}wiNhYNdUptUNaKmud zmuqXygpwg|IvWfIL!ofkZtt(J@Ak#&tLr& z&?L~$pqk_lt!gG2$0XyJWE_)>V^V96Nd_Q}?!8}fyi{EC6^XTP<(O6sU^e_A2?|4i zwAz&-1D%{~lG5Vbr5N4rM0^$5e6gjdBvF%<%^mi0$NU~yX!@9>!#P&-9)_z2Hqrxw z?}5Shz~Fme@I5g29vFNN488{j-vfj1fx-76d-NcC^dNg^*-)(JJ$c!~^cS}Y4r}KI z^5o`*hc?_CBAs2g(Ij50%xSjieuL!nx~r>=dF1b5FVx2y`CedUIQ8CqVpHuQ{cn3%U? zs5I=yVv)`^Iy1O}84Rm-%SS~3W-rpMXDz~5MVPe+vnKsmA`HJM@nVJPVCbtK23VC;zIrY{ zOF3pK$1LTTr5v*);%Saq$}vkhW+}%k<(Q=$vy@|&a?DbWS;{d>IkTdbvExXHIi52I zk(d}kNd+@EM5&>-sW#zAB8p7YaB#Ny`Bf|x^w4QBT$r9UGpEH4p0j(yqHIfl%2%A3 zgYQ1VjM)E)9<)-OZQUc`0i~!!!gLvWxRdM&%V4z^yLBmtnHe6t7@{V&85v>^Gvr~0 zD*2hr(0KfpY5k;wb|2m-pDB%x${O&*vK?* z)YE(hr$}3Bg5{YY*p`TmfHUPlP1#3kj<+ zk_c?H43Q)XWPxE|Ij|YH2)F{c4)_4@ao`KUKLXzYo&tUiXj!L>9S1Yocs24U5PXIT zH(p!VJHy%Ar?xZ9+ohz1;7cjtm&309W6J!8^zl0ea#?h{-*fpn$L`G6(V6olRwU`7R!LgjKPsPQj$if`u+bJM$2v5`1WNN|N zKJc~=yzK*T`@q{i@U{=U?E`Q7z}r6Xwhz4R18@7l+rGSF(T5BmWoU$LjXKq2%Atsp z%quC1&Q0cP1t7=Btbqd7uySnRb3>jI@<~reMsD}vdV`iebegkxe)1~A(68pgVXZrl zRakhgJlKEq_H3w`M(mOPNquU_It_QKBlw70_2dga(gmxZq3(yb4oo-xrg+8NDt=1=;3PZx>> z!H##aFFG1eAp zZ%B3qW(*=*ijfOl)ZrK!*3MrseYkL>lfKUPB@{TbJ{OL7LKPL2t!+Jn9ZS|M>1b=S z?en(=7cJvIDfsWkJ@`3Kv6`r7X<$6!uNMCJxU3DbkN+puHM3pzvl58LE zZR;AzX6Dy4WHZCffs%0N!6m8Qw&s?$+M347KvO!~Gt%ryp$HC7J)*v1eI1!Ut6q1A zi_Qy*%@*lB(^qWJq7GE311-d}D5V~noc)|ENL8#^4XQ;)ue5N2`-vk(BV?z=3(cF& z;b`RpK5jdrr*|qJ;z8oT)UU1kNkJO6&O3k=OESDgJe5cltx8Gw)y&?_n>e-u*ach* z+zQ+Y+zUJaIN?`mC@2jD?Ne!Ryj<(ZY2Gfx>xYawJ)4eYA|8SAoo7VVGiDa|_P8<)p?mI6Z4O(N}lt~sA;&gYuQ&9LoCn5!73B93w^)-F+V zwZhZKAAL**;w3N%h)%zxAkozdl3;i##8XT!KST8*CDmQ!>V8$LpQ)CKZZQ*WDo^Nl z^+1R*pcR-0tN^wE7Xw!U*8{f!cLDo=2LXB4R_@*6T-Ql%lnk>@Ua->`Pd)6!fL350 zumacuTntwLaufh{E)3LLy>^buw+)=^PqGFf9FJtSxi zd+E1){!~}8ZL&XEH@ao4Z}Y@% zyRB9JptvWU5B5*^vAAdT&mBFrh)9lV zi=HY*;}Qm25J`;75>Bw6G;*cN^&Y8v&R&DFN7hpi6LML0XgE0RlEmhr;eI`MS=YF0 z*mT=Hr@XSNDeMj{@t#{=*InnY^SCAbJzV2Wjm%%%W;GDuN>Sf2lsSYAF&VPMKQQn4uO|z`;re+oESOHdJyLY5)x7v?hmK^+Ka+CIt4!c z>B0WNTTVUI_6Gg6y}lNIwk_vx?n;z5HQDdJEq(ACl?`>lq2YxqGZnRM{c&qDeWMvJ z^(4d9pnhnEi@bN@Rr13nb%IQwq0PM|{zcp(^+zATq>N~C+K7hbK-k#xiUTNxp33vY zdvXTQ44YIcsXKhwOf;UZCz~HR%j@@hqNSE4@>hK(>X|-rB{qdr{PF6tc&;yQtzz(O z!8Q9@25(d?x1KtHbyHH)WeJHu7dUen$R!My!W-@48{;93@YVSK!Xj?U;{MR01Y-kgALGN12otG4K_f74bWf%G}r(Q zHstv~fyjmi8=yfV;<>3*3^D^WW{_x3Cfp)s1z~oXtV(*VQM$^J6-CufVfID+kzih{*Oti+6m)zd+%JPa(dot0egEA`ZMfsIz7JfSQL!_9$ zB*!MHx+I&bn>Ulm7hI9GTVZ*?VaU6itSXJCZ3xp^K=*>bhYIKt$KA$w1@&*l=OimZ zkk1~$t3m|sjF>uS0V`%w6$^u!`8dhymoPe?F{j%Xo{ z%zmnD1uE2$NN17^qO_iy(czH0{ zZ2hEVw7)Y}of%#;FPum&IB!|o)>C?F!v~*F*q=3pYOCLVU|7lh0K$mWs^N3vmC&M+M%DZgWHi}V(KZGj1ea&tPV`5gGbfDqw3&M zb?~SNn`#RzFjMrFK*MB9bYzaYzMFlxE8n-xD&V+ zcmQ}5coKLXKme1NwBkjaSfE~PyCmx|VZC5QHIk`NhX29iiZa2eLWP~k33g}ni1 z4>uxbt~3#Dn_vT*cw|OJO1MZaWDXbsRsvgrOMuU^u&+s~+t zWAc=N9aoc@IE5FW3Nis9xPvJYY-mXWqB@*1QUitWR`W!sp<-+;T;UXsoR28bCZ(L0o^C83I$}}doVCdsgw~eYcSwmCkG6ds zKzxz}X5qS>jI_kuiE&a8UU7yFEfAA$Ey!KV3Rw&5sb!jKL5^CGyB6fG1-WZM?plz$ z7UZr4xuuFZ3)Ca*`~(o>7TcQZVF2W=$zNMSG{YdUq)lrC4ugAA<0uRQhe6;lGZ_Yf z!ys@N1P+71VGuYB0*681FbEt5fx{rM&d4r<3W^sI1g>{(CK00Mm5huySwP&yf|iS? zNy?6nsO>$AdG1lexlJsnBfuViw-@2uiu*{1FX>VuSY5;4CQeUclVf>>lO3aQvSSp^ z7+=O%i(;%rG1j6OYf+4~D8^b8V=aoY7R6YLBsC|-S`=d~im?{OSc_sp*}_|6`hP3i zdB6%_3ve-TC2&1(8*mq}4|ouG40sxlg0d2eoiZblV#sla7*RgO_bDDN#Z^;WHN{m^ zTs6g2Q(QI0Ra0Cw#Z^;W6|IGlklc5%1k1FTF>qEh>bRRqf5%0i*PRFfT2G%Sp($(X z)AmBE%>D>3-$*DAM1)`_>VfPThGy$#NkUbK#Bj7!qd`PW)j#0>;QHF z*8;Z!cLMhU4*(9$#-Q1l7V~PECBNpwQt-Wz>oM}ep1OdrLbg#Fy|XN;^mvXjm4DVuHh>3jh}O+hLxE4L5bmhoY>JcP3^K}S?tTy zeb4jkeCD%^CBgJlsbs3LDU$Zrr)s0a74fQ++Y?Wexod*0y~M^;`mttx=KG)i^!NY# z&+oY7ez*O#hpa%%A1n>VW7)D2yQD0}U7K-$eIKcNiFJnbk#O@UGO1$>D2k`afp3CI zHNm8sU{XymsV41rXwnQ+Bxw&vDtP({u?!pc6u zBEo-(U=@#nfGn5>yB^|hS``wPLJJS$)YBGYxG z%TRn56yKGn_%3eI#Vxv^_%0~E3ySZ8;=7>uE-1bWitmEryFivMD85Tm{9+zmY)7~< zrC9@IDT7o2n2l7$m3CE!EOI-Pa%Kh-?z+ma+)hRoqepfj_5 zH*vxaU>9&La4T>pa4+xx@F?&k@I2s1pv8+g5@<6Ls8rsR>d!{JF{K~}pe@BQDVQNH zPw5SUjS6flsznrT?Us-$rr&TsUK)$-NyXV!;V>c*fC2_ zBeUJeY&SC7jm&lo)f=_EIpF* zlrxB!0}+WtHcL;AN6YbOIUX&?qvd$C9FLac(Q-Umjz`P!XgMA&$D^V7vh-vbjGW;v z$KN&NPY&-CQs;A(9;s7^b?YJhLCSnb>kk69bzwwLkQkO#KWDv#AxcL~`hPIZL0GxP zLz{J2j5oD>SFg=E!hChyfHr8)h`XFo(td^|Fd`Td$y)+V3$+P}6&B0p@g;M8hx4BT zehp}5A{TLtETy~y8D1cF5Sxy%BR3+OTgHErci-gu8u#3>$!vXl-DQ2uN9w8O`<8#+gZ~x)^44B@SfgWH9D)VWPM#fi`t(L<0288G7@fBmDlqE{=9Z49ht}?!@1#+4(zB8O;)iRvbW;pef zOn3U^ThyJ@@eOc$GrpM6nqi$3RS+}HxjR`2ovL_4a0VuZ=>0kJd$N`ka0?EbfWF|pOeZ6_l zP%-CfgrBSg#4|L&XH3tSI5LZF{~7Z$Bc{GZ<_9Nz!8E==4lI~AKR55+m#>Nl^@%cAKR55+m#>Nl^@%cAKR55 z+m#>Nl^^2qLp(Y&N-UKAJn{I!T1mRXm;`bz;6}rFtJN@!ZJ4h>DY28qfL350umacu zTntfv-W}~q^B~{TtLtAZSiQ8S{j>Qsje?zniTCO~` zBYQagespB_1rq^`O4h=P?7@5dsd!E2ime@W!x6WOSeAxR`=lXp*BeL-S#Q1P07d{e zqMKmbO7^54uB1JZ^v|BybjAvO=!NGwc^*C_Djy^2L%j3xL(mv(^YUququQOH?I*W^ z8nBGde)5`&m)fUJKQgbFFPJue8m7%~)67UwcVeB2xai1+95_5NT}WOYwZakGUy+<~ z=}Ba6-0$pBDG(H0jUd0?c@!|{n4UtN;4L8}GZD=!1lo0IkDhb%6pv`Osi7v?KiC@@ z>?sQ_Y@CL97t}R2uSu3zZ?m2|y8p1cp|^=Xr{#mKy&Z?6p)4q!{rD4|1;y11ww?Bh zV%uqlVB7lN2iuO~4tE&ck+97;f~RAfm@rOyg@U{XTBtAYi_T-)X>-H2e6tzJE;>5C zxn{vOWbLr8Jnput!tsjEMX95q+BLHyTC_FkFLk@U?nt8AJ%8EJG0j@T;;02f%y(H5 z5NnNZcs9|ngN1OqXehm8jMylyd$el3NQpR8Oe~yk?D~U}AC21F2$yMxH_KmhktDN{ z32`iGa!C4K9$TXPjnppFG9^<(8k|gx*kxvuD#b!GU87?8g|vE-PkS z=(7=xxYQ3N8X*-)oZOG)BCmA`(P&q>#AW7l}$K%?tgngvM;_ zy~7V1!-(_)KDGuuJyBr#{xPSuyFMC8r6SS#59fF0FvIUNKgs_>svw;35+-rf!X1T~ zkYUm7xKL6b3IIaB42< zm!LPYv9biXPk<#Pfb}APl_h|cC4iMBfR!bHl_h|cC4iMBfR!bHl_h|cC4iMBfR#nJ z+7v5`?%*jlssPJO08@&|QPSFscvwtf7RSPp#KMwK#Pf&wuE^vVctua z_Y&s4gn2Jv-bxc`sq!OPKc(=Dmb@$Dd}3x8ve5@qZ=Edx@TRKNKv!yptRg zCS%v6(_BrHP-!cQyR%^NJ|Y(66K(QVlJ^?}`0OYl2$P-khejI23?l16-WonTg7gap zwPtcH#_9p3cjA&*goR_ib?X7l_Ec-rSdxNh1WS<5NU-Hy4i767cU6_sLr~H~+#r&; zM)SucFnGMMB+O_dJt9+1kYWKT9Gh(sDV);69GLENS7O&?C?)prK9CY*5&AWCE*$#p zhJL%D-)`u)8~W{re!HRHZs@lg`t62(yP;q4b$3I*-Oz70^xF;ncIPdm-I{(qJflek zZsB(1ix{4Cvic;7$|!FqqEL&|G0`ICZN6t1XS-+&U6vkqOJ=Thj;}US zWqE0%TOUof4)=uu&57#TaFt!}PIq?IbWSv0vh;%;ftYVzeM@PDFVxUh-_aMeg2A?q zwotJU%HYmrgS}%?ntp6~wrbrOAA4+1fA*#N6)Tqxrj|%bGuUJOR$~vv<@4Bc!Z;)0 z%CF)K{OovoYaSoY81>u)I?n}b>V4HEmKBKCmbXnb9S6>o+U|09DDHEw-F6t#h^A1g zy`U7Pv2H#9V|KGj<`*ew43$d)MI;P9!)KV!FaoP&V~O}BpZ$oW1N=&YNaQSWmPnk8 z{H|CDV#eCkEL^8~8rKQPfxxtunF%?NnC9B6mjigCSlHyl6{zEYzQvW}5G)_4l>KS)dv{p{Aj>!6mt9`J(QLTwo-ZXiL|YFHF`I4e`F( z3^{u*P3F>*(ayRk0fvGz*421+)2h>Y>j7rSM9CWwM+^xDFC^^93$92kn-3hRMtC%q zH#s7C_>$$}!%O!c2?vZ%e;N*m8e8hR$d>Y)r%+#O-~a(M($3R3 z{oH69vPV`SZdBQm6tCqPGo&@MBqkE>;}pjgLwXWykUEVbsYr5>RHGi^Eb+GD1YltO z9FaUk`Te+rW3S?ZX8=w{#&RT&<($17DSC(|F3B$ zF3qJ$!bdP{L<3oNE;5ovn8&n`&~(e!#U=YMH$ zF1;=>n(!MdT&Z@CpGn5WDF;AULjwnh!gb>=H?&f8kS{+m2YLQyt3YWXkP$F?oc;Ce ziw)^?NMIuqXdLQiI(z1vv-$3v!V#&4bG{}IA-`g>!3=i1Le}9G)0XZHa$v)>1CArJ zNC`7M?Ztc~hX9?(a7g1&Py3KYm+8LGSIE_EWe%@A{Vj(QSFc z*#_ZkgK)M%INKQ8Z4k~j2xl9Fvkk)82H|XjaJE4>+aR265Y9FTXB&jGEl)Vx@`SUE zn{U&Ea}EZ1M=Y3|d%s9b(v@tc)X*=|Ma4AwIhMCS-rAhf2N!-tC@l+$O~8H3O!^j;@>S-k*>$eI^T*0Y}%O*-{5BS)Id@{(nF$>x`r zEXzw49-U=*;X0;(;{<0+RDy=p5eB1T18Nn zEZVKdA^gl}y>Jxa$04qwRmV9nU4ZK2{%xk9{PEW-)q(7fq9Z>EnEdNLZCy9g@<|rG zOd>bEBN#?W;2t-DtK$T&j?2VI_RTn}={T$DIIHP6Y-^m=bez?6oYi!k)pVTIbez?6 zoYi!k)pVS|RiqKFHu*5A-~zBz5$x9FX?zXyxrUis1C6hN#@9gOYoPHp(D)i?d<`_d z1{z-jjjw^m*FfWIpz$@(_?kS8ugTN+8fcur0~W{ia~ABZIaaDHlDBpBk*L;s{#Z%Z zFa5w#8IC0*9z6HxdDsV^E$9(ihQw$5EOwrLwZQuF@oJy@aS0}-X`N#blMr3T5qN~q zhvC@s3c9Iiqzem#R;+8~(8P>&?hj8zmG=)Q)8i!o#tLO1QcBN2;@QF20Wfv|j2!@D z2f)|?Fm?cp9ROnoz}NvWb^weW0AmNh*a0whAdj(FnT!S3G?)~Oo#)(c?#imf9Z;I$ zcof!tJTjxlueAQ={+GN3bSp8Y4qHl4jKGH1S^qN~;Z{R=60@l7Ws|V-Lp#rl{XN04 z)q0VTXl1FE1>V3Nigpo6rYW2Qt{rM2G2iS*WlF1zzRoDu8P-K87IB?La2dhS4FwE+ zc@gq}G)N$?RP=KxjP}QGL6Oj1N!~T(aMo&9;qh8jT+@pFP4Du49F~^r@~-*mUj4b) zUJlp0U8fxG>Nh2{zG+LlmfIu>+%$9C55+5|b&^yY5kHIKGpGwFNl10ErJBS1JR-t- zrup@Y1{7&kYB^a^lBXl?O4E)*6nSyzNiBEs)VM=AcM@YxC7+_IhZywXLEM?H{#uJo zI4$KrNGwYaer3;|PhR%^YX?5|!4KYX$Id-pIW2A7d+_bn+YYWupO*fMzesbR1vpo~ zijbGHE;1!Kj;DD~8@D%h1#yqks`5CC$0QxgLyA|C20+CkGGaYwFHmVpMs9j}nCar! z%h#yqm8kQA=XtJj#bw8m8IDnr1v{diUDkhU^jWvTE#ZI1GrwIYZGK%czs!0&v)+mK z*jS)s){8dE8F4UwL}(yyoezV>P=DHdTcltYfV|FL%X3#QCuo{@-pJE zs)p#R5!lITGToqxUDk#;I0@w2+|0-iEk;W^5jol|MWc74oJlPyF)zi@2E>aIM>&h5 zoW)Vj;wWcvl(RU>Ssdjoj&c@9Ig6v5#Zk_v<7mQb=!#~Pvp5J%pLfElBrptbuJJIW z1tU2;As&V_HW6u_m)6?x3O=_07Xw!U*8{f!cLDo=2Z6_cr-2s$Cvgo4O)8WZ3exJT z%C@VJntoOO6A_AROcI&>fs)JpI2(|i%QMgpw!r(D7D}dN-auLW28r^rSP5QS1YyZ zXG*PJsnn^RO0A&|{@Sl9b=nh3t>2;4hAWlYxLB#p6wukSP^qmKD|H6fK9lR7#qZC) zN2zl;cK)MEUBokA@;jw=ep0DRKdjW{V@kaV$^MGZDfQ-mRcg=Im3kZB?WG9%HJ2%M z?ej|A!2RDH{(kE?Kw()mh(P;ms0!wNvSV(EA=JD>MK7{ z>L1ntdzE^C_kW1%{qsAO`qwup^-bR8-#F*rzNFM6bR~Y|38fx;hf?2bRO*QzEA{Wc zQ0hltRO-oJD)lpd_p^(DzftPhwMzZhElT}2v-;oHEA>l0fB8D#VWob>{;wIc-!OMC z{eaou1pJkWeq;Ata-MwMo(4N z{5LCW{Apz^!i2ZzO~9WkYYCy6OWvuhNosFQenVNyPgB;)*D32%uD^lA(k&DT-qxzD za~@OH`7bH!B79@7xkFj6`ww#eN0fEN+m&_IUnuKsJj**OmGw@ldtAqNH$JMY_wYP7 z@!Ol9Q`RjvE9(PWl=VTb`{6Gs>!Utp{q^gCj{x6O*2nM{f9$ou?ZDTR_3>{i>+YW@ z>k|tBp68Pszvny3`ZU!08Q$x2z~|qntb6}VS^GHm0Iu0D0$<`izr0Raf4@RmU;C}H z9(Z0^58kJ&|FcS2-{9WgWDLH&Oj(b7Kv|D&SJroil=XO%vcC6Kfb*XiRo3?zpMU3l zfAj`rJsDTlGu;0f?(=iT`R82!*&WLIFXrdD^OW`cy~=uVk+OdKh_Zh7YihnMQnq!C zvP+wk?fty6tE!dl|Fp6rrztyeyRwro?S>C2JB@cF^Fw8~|AVr7KBnx!A!QGLRoSC= zDSN?F${znKWlvB>e#v9XUOG?NlfP8rDW#9W1W$!yf+4ucc*$19h z_Wkcr_LnyR-0#ag`&Su*fB3Mnzy5t?KZvmKPm{`K&h2l!PTAk$T90!5$6iwQ_r9&{ zC;nF1|Chgiz!?1K2g?5Oi^_iTi<&!I)(ua7`-1z=uAcuZ6?Z+Y`1jC%eC7iA{HNg8 z+*4Ogm6qJBk9%R_nx9*5SIRzf>Jgg~@vaF~uJ`5d+2BLUZ~vw8TQ3qspNHR~E3j{W zRRyds(?Wexgu~^-oST!&VbLV8hval-;sSzQCz?EpQu-^&PJ9 zFtCN|*s5kWu&+@;*N;`uzKHYADS|xHA@CEGvi?VO@66Og`cw7+^&hI$dXuWNTBdGNzr}Ay zCK+>+wjRPZAou=n;0_hGKBk*Wp9bLz3F zU)rB%&i|hIZUDMD|KAv^2l#vk!ZxeH&s(fOf`WsP2fxu zIMYqlUrc*JP}q$u7Z3_2*MpS=9GJ9*xo9z zPLV+T0y^J!5Lh3hAk)uPm3mfHG0s)?)vC&ReCmgGSk=4gn4>M+=a#8YTR*06?TgI2 z{4L}BZ>kGC>2i$%3;5ePraxsr5BEZ63z?hu@{E^q{Fl(pOMrZT55L{%Tw~F+-~0+3 z`w*VDX<)AgAK=EWr>BrNOU{5#yGoEagl`xB@#ZeuW8I8)r0g%+H}cK9%_n2qr+!lo z8oT*DoZWS`Qr(LXjrmLeOYu)FQmNmTn0v4W{Dv>|9jxb|S*23SrH@$h4@b)jo+(ro z&(uLwVeiyS$o=JPD^vyBN>$0Wijw%hgA@7KR@0Q?cUY@y*!q=!>bI!80k%OEWE)Z; zwqX^X`VCkVVH;IZwlV3ZhT0ULdJ&9Cu&q_KZ0l4V+oVcPy?~ln&lW6an^GyZjjC}9 z>_%z$HF%L`+bmVhz=4*j=fS!R+pNm6ZB?yob1FCWEBxebY}@Gr^DDyUI@op+y!{*_ z-^I3Db+heJJ#2eb@6`V=qJ3=pRX^JSHNbXI4Nm@5XXS+ZxnEEdq+v98(s)cM9sYPrj)Wp;;)MB-m?Gm+w?NYUr?J~7&>RD9U zNw&+?a<(hf3brfN$|HZb9~rq*sfJ;*`B6OW4lhRoBA1E z?e%Oos10mSSEsYxs5VYLgA~7s?Pj%^?H09#?N+sQ>Zd5=XRtj}oym5a+QxRf+CKHP zI!m3!_H1=F+jG=8Y|mBaPCcd0Q|GZgU!BkP0(Ak~3)O{FPf{Oj2iuF(MQkrt7qh)Y zT{86(^&0gWwtu4jgzZkXlkIEOYo~s!UZ-Bi_EL2z+so8tY+tWlKlLB#a&fwT zeWQ9K+c&8iwO8$B`wsOEw%4d@*uGP}bL#(6*X&xh*Qx8+UaziadxN@R z>igd zQ?~!FyEB28syGw;s_|8H#Mg|k5q{nHI^j2rZxDXd_@?=yaf5LK;kS%$5q=wK_oDF~<2&XH z#&?bH5`NG49^v;{J{7D;Z4R(gg-QXNcbPdf0)m)9@~!ye{B4i@F&Jk z2>;XgPd4?s*|?eTr^ZhSe`frQ@aM+Q%~i%Pj9(D`()cCeN@FGAuZ&-r<BDw-DZH z+)DUs+ixZQlpxWl-E@OQ@V2=6rRB)rSG%Y4$f z+qj$X_vpbVjeCrH2=6uSHJ>p4VElpbkH#Mf?=$Wryx+Lre4KUU9w2k_g;iJZ*gdFCh#^c80gijbx5I$)@|7`_ZoZ6Kd`pmKEk(* zw+P=h-X`2{>^JX4p1(tQz&JqoU&enC{@M7md5`h`jQ>Yy8YZEvuV<)>JU*M#;da(I zhtuU?#}kK>h|}S6I$d^`-EOnnop!rRJGaZ_lnD3jBzs&=w{}hs>wNGN=XL|dVY3rT zCOgS)x6k9KtE;ow>~(AP1WqYXp6Q43%`dJ)9^EAWj#! z$TPrn)!96P5YK{=AkBkHBdx(uzCZ>5_XREl;EE!tk}d({NT3onr`JzqA(|!`l@kZa zEvi!oxn`b!NcTnHnyBd6Q#_kunSLUQTiHnb2t z1dc^xkhcS#fQM*pQZJ#8>E@-UV#cE-%5X?f6Mz&G9pa#Q`MzI1JHk6hSWIP_ARcNf+5#%HR z?sWPB(AXm+t~#d+0(&&IpfR`H!qq_1WDa)G1sDBSlfJ z#}n{7HH|g%c`O>+G>vuBYl2b{+J``B42ww>Y&N%-Lz)<``wI1R`n!fjfE1LC7sY1lKONdJfxBy02!gW$K$8r9d1oycOQ-2HXHc`s)k{!vwMWi?b7;A z8#NTxsM6TU(_SxNv=e5h1RwZ1br*K|*+7rICdCPhYC;1Jjzko|@6gjzp)t=;duZvi zPC{%-bNf9ogV*Ery8RUzQ;5$eOl_k>NQb5tPN&x!3_t^@Y|$73a=^=wP`8WZqjO3h zAP^cOd^8E6vB&EXk%T8Nkg0&*=Y^nTASW3C5sJ7ywXI z!tM%$=(KQR#c3LQU2d-xaU%$zF)RrvSU{6fXe`bPEdb+zC!jIT9S|rMo&A1l;1nt$ zt}Pn-e4(I7S>Xig0uD$nkQL%c)AN%GRr-LS?A8LF+SoikFO5c_cxisB<#Y!8&=_$f zyhRFN+`z}zK{9A8)Rb!q@px!oDZ)!C=^;VJFRT!hlNSJVAKIHoP(XH%%}%n=SWpxi zLzD`QUGx{1rZFuJw@WnvB=sc@W{^NL0Fb0c?ofov1}qwReHO8OZV&Pbhg$G^z3kD) zLud_znsx!0R3cQH(!3#`*YEcS=yV}Xdr0qg2ZKUzJ8gutp=8qh{z%v@I>e$ejZmR6 zRiY2kBR~=4ow|Lbif$i|xj*gh8AE z`n40bgT^3f(Kr}@ptP%7XiT%wv)y6Zk^CZ|c^oMtG^XYjjfD~}%A>ysjiV823=$TN zqaYK3xPd@KPJYoEKA%NnSi|A7+e5TFsnE@$F-<^5X<SD8MbIlg%N7vLOK*eYZ^y1je`ibNFR+u zp;{Wl?Se)uM*R=c*l*ETY=#$ll`vKVNEojA667cWvpHg?cv#g z$XN=c0U%@`5R4!Q;8SAKCINVY5^(+)a1j0?wIh)*m^eIs5!Vz=xuH-ZAqEi?5i1Y^ zQ31pWih;Y38HN+_;-Y;B1iJu8dc-^mhC|_yM+ywUiI7Q*r6^^15J&W1k^n>L1U^hu zp|Mx=hFrTm_CPQm7Y*(etSBlW$V7xR`V^p1_j)f38-|iJ zJ`Q4e;T&igg$P1ZSW8+Y&Q{pk1rq{AKzM!8XjHV6H^`yomKU<8Q$A>n8W$ReL!cw# z1YrTPLS|S(3^c*l&5!N`k|=DuBNUB9Bi>*r;0*>PFOa?7IAwV4NDrYgW}Ji43H;=T zPIezqAy>ff_BukLWD+!Jdo(tWrv#a(utJKHCnTygsLfc>-$*LEBSs8+RJRkx0epx~ zmEP%dcrgUg$*`mkZbwOYywsP{#JTAHR3;XUV1WtY{OO!06o^|C3x%O5F5(XgN1_%& z0q7aAxaJ|6~e(sbRmBbT)E~>oIn~Titq{q zk<77FGL=MUMi8U)ER;A6nN6i&S~wd;5%6QvQ4I+&p?p}wLE#v=_WGT%SfK#h1p=u+ z2#FIY2r?YDWU z0zm{=Dw(7-q%BW~EcIuSAgAR*xKQ#%Lg@-)#)YB7p?ENo(jqq=4JRX!xGzSF(B^?4 zjf2bRFrJ7dq)lRlL@cRYtN@?G_^?wjlgS8;gRx)?L)7oX21un!rI3hRH;m$g3x%&> zNEkVcn8IFxXMIo~UIN_+jm5N!u~&{&+uABCa(SxL~=L8R2=kA-u>n9*1a`7bUR zie55a9YzcZ;><=Pr>EkL@^Z?3Z&DC62y;5p9qC>xx8qpa6A;( z5{Pm$nZ~BDRw}3%zdxPwQzOI)Z!$CtWENbx_5+GQ#846>wZcwiaiK&qj$B0-1Vd=h zV0|tZ4mo{c%$ZO)h^unhXPko^tIAxss`JnYIS65F#!REQ3sAE+9C`DG4Wf z6AAs=7Yv3y?zq!k;ORgRE)9r%?@Phv(){#icPJWm(GamjD4g{LLSYm|E}JF4I4`0X zjw)nxKoX+I8kz%%XimC%JdsGkwYXF$mKAOcr{Ym~HXTgLv!P@toJptB=~N+QxkOrM zoGhghaBVh|Ni+Z>E)YWDe7>M*oCqZ__9-Y7%4VCJBcg^ec<9`jG=0|}z^rk*gvOMB zT;MtYGy-TGO@;y%jnlMQE}W!MQ>joG_$cyFF`tiwTz(i;Xw3E`G_eG{MhK0gF))y8 zztFg`5t4xy8aomUHG)i2KB#fxltSQm6G>h%q^8K+gwtIg1f?LXFa5~rOUI)a5-9{r z#2v=iMkmJ;p(r$_jf0_lE+;)io(RCHngLK{TnA l=zbo|8hLz${wQW+TahP%4s5 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 BGSdJ6 literal 0 HcmV?d00001 diff --git a/test_proj/addons/gut/fonts/OFL.txt b/test_proj/addons/gut/fonts/OFL.txt new file mode 100644 index 0000000..68d1d00 --- /dev/null +++ b/test_proj/addons/gut/fonts/OFL.txt @@ -0,0 +1,94 @@ +Copyright (c) 2009, Mark Simonson (http://www.ms-studio.com, mark@marksimonson.com), +with Reserved Font Name Anonymous Pro. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/test_proj/addons/gut/gut.gd b/test_proj/addons/gut/gut.gd new file mode 100644 index 0000000..f4dd956 --- /dev/null +++ b/test_proj/addons/gut/gut.gd @@ -0,0 +1,1463 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# View readme for usage details. +# ############################################################################## +extends Control + +# -- Settings -- +var _select_script = '' +var _tests_like = '' +var _inner_class_name = '' +var _should_maximize = false setget set_should_maximize, get_should_maximize +var _log_level = 1 setget set_log_level, get_log_level +var _disable_strict_datatype_checks = false setget disable_strict_datatype_checks, is_strict_datatype_checks_disabled +var _test_prefix = 'test_' +var _file_prefix = 'test_' +var _file_extension = '.gd' +var _inner_class_prefix = 'Test' +var _temp_directory = 'user://gut_temp_directory' +var _export_path = '' setget set_export_path, get_export_path +var _include_subdirectories = false setget set_include_subdirectories, get_include_subdirectories +var _double_strategy = 1 setget set_double_strategy, get_double_strategy +var _pre_run_script = '' setget set_pre_run_script, get_pre_run_script +var _post_run_script = '' setget set_post_run_script, get_post_run_script +var _color_output = false setget set_color_output, get_color_output +# -- End Settings -- + + +# ########################### +# Other Vars +# ########################### +const LOG_LEVEL_FAIL_ONLY = 0 +const LOG_LEVEL_TEST_AND_FAILURES = 1 +const LOG_LEVEL_ALL_ASSERTS = 2 +const WAITING_MESSAGE = '/# waiting #/' +const PAUSE_MESSAGE = '/# Pausing. Press continue button...#/' + +var _utils = load('res://addons/gut/utils.gd').get_instance() +var _lgr = _utils.get_logger() +var _strutils = _utils.Strutils.new() +# Used to prevent multiple messages for deprecated setup/teardown messages +var _deprecated_tracker = _utils.ThingCounter.new() + +# The instance that is created from _pre_run_script. Accessible from +# get_pre_run_script_instance. +var _pre_run_script_instance = null +var _post_run_script_instance = null # This is not used except in tests. + + +var _script_name = null +var _test_collector = _utils.TestCollector.new() + +# The instanced scripts. This is populated as the scripts are run. +var _test_script_objects = [] + +var _waiting = false +var _done = false +var _is_running = false + +var _current_test = null +var _log_text = "" + +var _pause_before_teardown = false +# when true _pause_before_teardown will be ignored. useful +# when batch processing and you don't want to watch. +var _ignore_pause_before_teardown = false +var _wait_timer = Timer.new() + +var _yield_between = { + should = false, + timer = Timer.new(), + after_x_tests = 5, + tests_since_last_yield = 0 +} + +var _was_yield_method_called = false +# used when yielding to gut instead of some other +# signal. Start with set_yield_time() +var _yield_timer = Timer.new() + +var _unit_test_name = '' +var _new_summary = null + +var _yielding_to = { + obj = null, + signal_name = '' +} + +var _stubber = _utils.Stubber.new() +var _doubler = _utils.Doubler.new() +var _spy = _utils.Spy.new() +var _gui = null +var _orphan_counter = _utils.OrphanCounter.new() +var _autofree = _utils.AutoFree.new() + +# This is populated by test.gd each time a paramterized test is encountered +# for the first time. +var _parameter_handler = null + +# Used to cancel importing scripts if an error has occurred in the setup. This +# prevents tests from being run if they were exported and ensures that the +# error displayed is seen since importing generates a lot of text. +var _cancel_import = false + +const SIGNAL_TESTS_FINISHED = 'tests_finished' +const SIGNAL_STOP_YIELD_BEFORE_TEARDOWN = 'stop_yield_before_teardown' +const SIGNAL_PRAMETERIZED_YIELD_DONE = 'parameterized_yield_done' + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _init(): + # When running tests for GUT itself, _utils has been setup to always return + # a new logger so this does not set the gut instance on the base logger + # when creating test instances of GUT. + _lgr.set_gut(self) + + add_user_signal(SIGNAL_TESTS_FINISHED) + add_user_signal(SIGNAL_STOP_YIELD_BEFORE_TEARDOWN) + add_user_signal('timeout') + add_user_signal('done_waiting') + add_user_signal(SIGNAL_PRAMETERIZED_YIELD_DONE) + _doubler.set_output_dir(_temp_directory) + _doubler.set_stubber(_stubber) + _doubler.set_spy(_spy) + _doubler.set_gut(self) + + # TODO remove these, universal logger should fix this. + _doubler.set_logger(_lgr) + _spy.set_logger(_lgr) + _stubber.set_logger(_lgr) + _test_collector.set_logger(_lgr) + + _gui = load('res://addons/gut/GutScene.tscn').instance() + +# ------------------------------------------------------------------------------ +# Initialize controls +# ------------------------------------------------------------------------------ +func _ready(): + if(!_utils.is_version_ok()): + _print_versions() + push_error(_utils.get_bad_version_text()) + print('Error: ', _utils.get_bad_version_text()) + get_tree().quit() + return + + _lgr.info(str('using [', OS.get_user_data_dir(), '] for temporary output.')) + + set_process_input(true) + + add_child(_wait_timer) + _wait_timer.set_wait_time(1) + _wait_timer.set_one_shot(true) + + add_child(_yield_between.timer) + _wait_timer.set_one_shot(true) + + add_child(_yield_timer) + _yield_timer.set_one_shot(true) + _yield_timer.connect('timeout', self, '_yielding_callback') + + _setup_gui() + + if(_select_script != null): + select_script(_select_script) + + if(_tests_like != null): + set_unit_test_name(_tests_like) + + if(_should_maximize): + # GUI checks for is_in_tree will not pass yet. + call_deferred('maximize') + + # hide the panel that IS gut so that only the GUI is seen + self.self_modulate = Color(1,1,1,0) + show() + _print_versions() + +# ------------------------------------------------------------------------------ +# Runs right before free is called. Can't override `free`. +# ------------------------------------------------------------------------------ +func _notification(what): + if(what == NOTIFICATION_PREDELETE): + for test_script in _test_script_objects: + if(is_instance_valid(test_script)): + test_script.free() + + _test_script_objects = [] + + if(is_instance_valid(_gui)): + _gui.free() + +func _print_versions(send_all = true): + var info = _utils.get_version_text() + + if(send_all): + p(info) + else: + var printer = _lgr.get_printer('gui') + printer.send(info + "\n") + + +# ############################################################################## +# +# GUI Events and setup +# +# ############################################################################## +func _setup_gui(): + # This is how we get the size of the control to translate to the gui when + # the scene is run. This is also another reason why the min_rect_size + # must match between both gut and the gui. + _gui.rect_size = self.rect_size + add_child(_gui) + _gui.set_anchor(MARGIN_RIGHT, ANCHOR_END) + _gui.set_anchor(MARGIN_BOTTOM, ANCHOR_END) + _gui.connect('run_single_script', self, '_on_run_one') + _gui.connect('run_script', self, '_on_new_gui_run_script') + _gui.connect('end_pause', self, '_on_new_gui_end_pause') + _gui.connect('ignore_pause', self, '_on_new_gui_ignore_pause') + _gui.connect('log_level_changed', self, '_on_log_level_changed') + var _foo = connect('tests_finished', _gui, 'end_run') + +func _add_scripts_to_gui(): + var scripts = [] + for i in range(_test_collector.scripts.size()): + var s = _test_collector.scripts[i] + var txt = '' + if(s.has_inner_class()): + txt = str(' - ', s.inner_class_name, ' (', s.tests.size(), ')') + else: + txt = str(s.get_full_name(), ' (', s.tests.size(), ')') + scripts.append(txt) + _gui.set_scripts(scripts) + +func _on_run_one(index): + clear_text() + var indexes = [index] + if(!_test_collector.scripts[index].has_inner_class()): + indexes = _get_indexes_matching_path(_test_collector.scripts[index].path) + _test_the_scripts(indexes) + +func _on_new_gui_run_script(index): + var indexes = [] + clear_text() + for i in range(index, _test_collector.scripts.size()): + indexes.append(i) + _test_the_scripts(indexes) + +func _on_new_gui_end_pause(): + _pause_before_teardown = false + emit_signal(SIGNAL_STOP_YIELD_BEFORE_TEARDOWN) + +func _on_new_gui_ignore_pause(should): + _ignore_pause_before_teardown = should + +func _on_log_level_changed(value): + set_log_level(value) + +##################### +# +# Events +# +##################### + +# ------------------------------------------------------------------------------ +# Timeout for the built in timer. emits the timeout signal. Start timer +# with set_yield_time() +# ------------------------------------------------------------------------------ +func _yielding_callback(from_obj=false): + _lgr.end_yield() + if(_yielding_to.obj): + _yielding_to.obj.call_deferred( + "disconnect", + _yielding_to.signal_name, self, + '_yielding_callback') + _yielding_to.obj = null + _yielding_to.signal_name = '' + + if(from_obj): + # we must yiled for a little longer after the signal is emitted so that + # the signal can propagate to other objects. This was discovered trying + # to assert that obj/signal_name was emitted. Without this extra delay + # the yield returns and processing finishes before the rest of the + # objects can get the signal. This works b/c the timer will timeout + # and come back into this method but from_obj will be false. + _yield_timer.set_wait_time(.1) + _yield_timer.start() + else: + emit_signal('timeout') + +# ------------------------------------------------------------------------------ +# completed signal for GDScriptFucntionState returned from a test script that +# has yielded +# ------------------------------------------------------------------------------ +func _on_test_script_yield_completed(): + _waiting = false + +##################### +# +# Private +# +##################### +func _log_test_children_warning(test_script): + if(!_lgr.is_type_enabled(_lgr.types.orphan)): + return + + var kids = test_script.get_children() + if(kids.size() > 0): + var msg = '' + if(_log_level == 2): + msg = "Test script still has children when all tests finisehd.\n" + for i in range(kids.size()): + msg += str(" ", _strutils.type2str(kids[i]), "\n") + msg += "You can use autofree, autoqfree, add_child_autofree, or add_child_autoqfree to automatically free objects." + else: + msg = str("Test script has ", kids.size(), " unfreed children. Increase log level for more details.") + + + _lgr.warn(msg) + +# ------------------------------------------------------------------------------ +# Convert the _summary dictionary into text +# ------------------------------------------------------------------------------ +func _print_summary(): + _lgr.log("\n\n*** Run Summary ***", _lgr.fmts.yellow) + + _new_summary.log_summary_text(_lgr) + + var logger_text = '' + if(_lgr.get_errors().size() > 0): + logger_text += str("\n* ", _lgr.get_errors().size(), ' Errors.') + if(_lgr.get_warnings().size() > 0): + logger_text += str("\n* ", _lgr.get_warnings().size(), ' Warnings.') + if(_lgr.get_deprecated().size() > 0): + logger_text += str("\n* ", _lgr.get_deprecated().size(), ' Deprecated calls.') + if(logger_text != ''): + logger_text = "\nWarnings/Errors:" + logger_text + "\n\n" + _lgr.log(logger_text) + + if(_new_summary.get_totals().tests > 0): + var fmt = _lgr.fmts.green + var msg = str(_new_summary.get_totals().passing) + ' passed ' + str(_new_summary.get_totals().failing) + ' failed. ' + \ + str("Tests finished in ", _gui.elapsed_time_as_str()) + if(_new_summary.get_totals().failing > 0): + fmt = _lgr.fmts.red + elif(_new_summary.get_totals().pending > 0): + fmt = _lgr.fmts.yellow + + _lgr.log(msg, fmt) + else: + _lgr.log('No tests ran', _lgr.fmts.red) + + +func _validate_hook_script(path): + var result = { + valid = true, + instance = null + } + + # empty path is valid but will have a null instance + if(path == ''): + return result + + var f = File.new() + if(f.file_exists(path)): + var inst = load(path).new() + if(inst and inst is _utils.HookScript): + result.instance = inst + result.valid = true + else: + result.valid = false + _lgr.error('The hook script [' + path + '] does not extend res://addons/gut/hook_script.gd') + else: + result.valid = false + _lgr.error('The hook script [' + path + '] does not exist.') + + return result + + +# ------------------------------------------------------------------------------ +# Runs a hook script. Script must exist, and must extend +# res://addons/gut/hook_script.gd +# ------------------------------------------------------------------------------ +func _run_hook_script(inst): + if(inst != null): + inst.gut = self + inst.run() + return inst + +# ------------------------------------------------------------------------------ +# Initialize variables for each run of a single test script. +# ------------------------------------------------------------------------------ +func _init_run(): + var valid = true + _test_collector.set_test_class_prefix(_inner_class_prefix) + _test_script_objects = [] + _new_summary = _utils.Summary.new() + + _log_text = "" + + _current_test = null + + _is_running = true + + _yield_between.tests_since_last_yield = 0 + + var pre_hook_result = _validate_hook_script(_pre_run_script) + _pre_run_script_instance = pre_hook_result.instance + var post_hook_result = _validate_hook_script(_post_run_script) + _post_run_script_instance = post_hook_result.instance + + valid = pre_hook_result.valid and post_hook_result.valid + + return valid + + +# ------------------------------------------------------------------------------ +# Print out run information and close out the run. +# ------------------------------------------------------------------------------ +func _end_run(): + _gui.end_run() + _print_summary() + p("\n") + + # Do not count any of the _test_script_objects since these will be released + # when GUT is released. + _orphan_counter._counters.total += _test_script_objects.size() + if(_orphan_counter.get_counter('total') > 0 and _lgr.is_type_enabled('orphan')): + _orphan_counter.print_orphans('total', _lgr) + p("Note: This count does not include GUT objects that will be freed upon exit.") + p(" It also does not include any orphans created by global scripts") + p(" loaded before tests were ran.") + p(str("Total orphans = ", _orphan_counter.orphan_count())) + + if(!_utils.is_null_or_empty(_select_script)): + p('Ran Scripts matching "' + _select_script + '"') + if(!_utils.is_null_or_empty(_unit_test_name)): + p('Ran Tests matching "' + _unit_test_name + '"') + if(!_utils.is_null_or_empty(_inner_class_name)): + p('Ran Inner Classes matching "' + _inner_class_name + '"') + + # For some reason the text edit control isn't scrolling to the bottom after + # the summary is printed. As a workaround, yield for a short time and + # then move the cursor. I found this workaround through trial and error. + _yield_between.timer.set_wait_time(0.1) + _yield_between.timer.start() + yield(_yield_between.timer, 'timeout') + _gui.scroll_to_bottom() + + _is_running = false + update() + _run_hook_script(_post_run_script_instance) + emit_signal(SIGNAL_TESTS_FINISHED) + + _gui.set_title("Finished.") + + +# ------------------------------------------------------------------------------ +# Checks the passed in thing to see if it is a "function state" object that gets +# returned when a function yields. +# ------------------------------------------------------------------------------ +func _is_function_state(script_result): + return script_result != null and \ + typeof(script_result) == TYPE_OBJECT and \ + script_result is GDScriptFunctionState + +# ------------------------------------------------------------------------------ +# Print out the heading for a new script +# ------------------------------------------------------------------------------ +func _print_script_heading(script): + if(_does_class_name_match(_inner_class_name, script.inner_class_name)): + var fmt = _lgr.fmts.underline + var divider = '-----------------------------------------' + + var text = '' + if(script.inner_class_name == null): + text = script.path + else: + text = script.path + '.' + script.inner_class_name + _lgr.log("\n\n" + text, fmt) + + if(!_utils.is_null_or_empty(_inner_class_name) and _does_class_name_match(_inner_class_name, script.inner_class_name)): + _lgr.log(str(' [',script.inner_class_name, '] matches [', _inner_class_name, ']'), fmt) + + if(!_utils.is_null_or_empty(_unit_test_name)): + _lgr.log(' Only running tests like: "' + _unit_test_name + '"', fmt) + + +# ------------------------------------------------------------------------------ +# Just gets more logic out of _test_the_scripts. Decides if we should yield after +# this test based on flags and counters. +# ------------------------------------------------------------------------------ +func _should_yield_now(): + var should = _yield_between.should and \ + _yield_between.tests_since_last_yield == _yield_between.after_x_tests + if(should): + _yield_between.tests_since_last_yield = 0 + else: + _yield_between.tests_since_last_yield += 1 + return should + +# ------------------------------------------------------------------------------ +# Yes if the class name is null or the script's class name includes class_name +# ------------------------------------------------------------------------------ +func _does_class_name_match(the_class_name, script_class_name): + return (the_class_name == null or the_class_name == '') or (script_class_name != null and script_class_name.findn(the_class_name) != -1) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _setup_script(test_script): + test_script.gut = self + test_script.set_logger(_lgr) + add_child(test_script) + _test_script_objects.append(test_script) + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _do_yield_between(time): + _yield_between.timer.set_wait_time(time) + _yield_between.timer.start() + return _yield_between.timer + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _wait_for_done(result): + var iter_counter = 0 + var print_after = 3 + + # sets waiting to false. + result.connect('completed', self, '_on_test_script_yield_completed') + + if(!_was_yield_method_called): + _lgr.log('-- Yield detected, waiting --', _lgr.fmts.yellow) + + _was_yield_method_called = false + _waiting = true + _wait_timer.set_wait_time(0.4) + + var dots = '' + while(_waiting): + iter_counter += 1 + _lgr.yield_text('waiting' + dots) + _wait_timer.start() + yield(_wait_timer, 'timeout') + dots += '.' + if(dots.length() > 5): + dots = '' + + _lgr.end_yield() + emit_signal('done_waiting') + +# ------------------------------------------------------------------------------ +# returns self so it can be integrated into the yield call. +# ------------------------------------------------------------------------------ +func _wait_for_continue_button(): + p(PAUSE_MESSAGE, 0) + _waiting = true + return self + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _call_deprecated_script_method(script, method, alt): + if(script.has_method(method)): + var txt = str(script, '-', method) + if(!_deprecated_tracker.has(txt)): + # Removing the deprecated line. I think it's still too early to + # start bothering people with this. Left everything here though + # because I don't want to remember how I did this last time. + #_lgr.deprecated(str('The method ', method, ' has been deprecated, use ', alt, ' instead.')) + _deprecated_tracker.add(txt) + script.call(method) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _get_indexes_matching_script_name(name): + var indexes = [] # empty runs all + for i in range(_test_collector.scripts.size()): + if(_test_collector.scripts[i].get_filename().find(name) != -1): + indexes.append(i) + return indexes + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _get_indexes_matching_path(path): + var indexes = [] + for i in range(_test_collector.scripts.size()): + if(_test_collector.scripts[i].path == path): + indexes.append(i) + return indexes + +# ------------------------------------------------------------------------------ +# Execute all calls of a parameterized test. +# ------------------------------------------------------------------------------ +func _parameterized_call(test_script): + var script_result = test_script.call(_current_test.name) + if(_is_function_state(script_result)): + _wait_for_done(script_result) + yield(self, 'done_waiting') + + if(_parameter_handler == null): + _lgr.error(str('Parameterized test ', _current_test.name, ' did not call use_parameters for the default value of the parameter.')) + _fail(str('Parameterized test ', _current_test.name, ' did not call use_parameters for the default value of the parameter.')) + else: + while(!_parameter_handler.is_done()): + script_result = test_script.call(_current_test.name) + if(_is_function_state(script_result)): + _wait_for_done(script_result) + yield(self, 'done_waiting') + script_result = null + _parameter_handler = null + emit_signal(SIGNAL_PRAMETERIZED_YIELD_DONE) + + +# ------------------------------------------------------------------------------ +# Run all tests in a script. This is the core logic for running tests. +# +# Note, this has to stay as a giant monstrosity of a method because of the +# yields. +# ------------------------------------------------------------------------------ +func _test_the_scripts(indexes=[]): + _orphan_counter.add_counter('total') + + _print_versions(false) + var is_valid = _init_run() + if(!is_valid): + _lgr.error('Something went wrong and the run was aborted.') + return + + _run_hook_script(_pre_run_script_instance) + if(_pre_run_script_instance!= null and _pre_run_script_instance.should_abort()): + _lgr.error('pre-run abort') + emit_signal(SIGNAL_TESTS_FINISHED) + return + + _gui.run_mode() + + var indexes_to_run = [] + if(indexes.size()==0): + for i in range(_test_collector.scripts.size()): + indexes_to_run.append(i) + else: + indexes_to_run = indexes + + _gui.set_progress_script_max(indexes_to_run.size()) # New way + _gui.set_progress_script_value(0) + + if(_doubler.get_strategy() == _utils.DOUBLE_STRATEGY.FULL): + _lgr.info("Using Double Strategy FULL as default strategy. Keep an eye out for weirdness, this is still experimental.") + + # loop through scripts + for test_indexes in range(indexes_to_run.size()): + var the_script = _test_collector.scripts[indexes_to_run[test_indexes]] + _orphan_counter.add_counter('script') + + if(the_script.tests.size() > 0): + _gui.set_title(the_script.get_full_name()) + _lgr.set_indent_level(0) + _print_script_heading(the_script) + _new_summary.add_script(the_script.get_full_name()) + + var test_script = the_script.get_new() + var script_result = null + _setup_script(test_script) + _doubler.set_strategy(_double_strategy) + + # yield between test scripts so things paint + if(_yield_between.should): + yield(_do_yield_between(0.01), 'timeout') + + # !!! + # Hack so there isn't another indent to this monster of a method. if + # inner class is set and we do not have a match then empty the tests + # for the current test. + # !!! + if(!_does_class_name_match(_inner_class_name, the_script.inner_class_name)): + the_script.tests = [] + else: + # call both pre-all-tests methods until prerun_setup is removed + _call_deprecated_script_method(test_script, 'prerun_setup', 'before_all') + test_script.before_all() + + _gui.set_progress_test_max(the_script.tests.size()) # New way + + # Each test in the script + for i in range(the_script.tests.size()): + _stubber.clear() + _spy.clear() + _doubler.clear_output_directory() + _current_test = the_script.tests[i] + + if((_unit_test_name != '' and _current_test.name.find(_unit_test_name) > -1) or + (_unit_test_name == '')): + _lgr.log_test_name() + _lgr.set_indent_level(1) + _orphan_counter.add_counter('test') + + # yield so things paint + if(_should_yield_now()): + yield(_do_yield_between(0.001), 'timeout') + + _call_deprecated_script_method(test_script, 'setup', 'before_each') + test_script.before_each() + + # When the script yields it will return a GDScriptFunctionState object + if(_current_test.arg_count > 1): + _lgr.error(str('Parameterized test ', _current_test.name, ' has too many parameters: ', _current_test.arg_count, '.')) + elif(_current_test.arg_count == 1): + script_result = _parameterized_call(test_script) + if(_is_function_state(script_result)): + yield(self, SIGNAL_PRAMETERIZED_YIELD_DONE) + script_result = null + else: + script_result = test_script.call(_current_test.name) + _new_summary.add_test(_current_test.name) + + + if(_is_function_state(script_result)): + _wait_for_done(script_result) + yield(script_result, 'completed') + _lgr.end_yield() + + #if the test called pause_before_teardown then yield until + #the continue button is pressed. + if(_pause_before_teardown and !_ignore_pause_before_teardown): + _gui.pause() + yield(_wait_for_continue_button(), SIGNAL_STOP_YIELD_BEFORE_TEARDOWN) + + test_script.clear_signal_watcher() + + + # call each post-each-test method until teardown is removed. + _call_deprecated_script_method(test_script, 'teardown', 'after_each') + test_script.after_each() + + # Free up everything in the _autofree. Yield for a bit if we + # have anything with a queue_free so that they have time to + # free and are not found by the orphan counter. + var aqf_count = _autofree.get_queue_free_count() + _autofree.free_all() + if(aqf_count > 0): + yield(_do_yield_between(0.01), 'timeout') + # ------ + + if(_log_level > 0): + _orphan_counter.print_orphans('test', _lgr) + + _current_test.has_printed_name = false + _gui.set_progress_test_value(i + 1) + _doubler.get_ignored_methods().clear() + + _current_test = null + _lgr.dec_indent() + _orphan_counter.print_orphans('script', _lgr) + # call both post-all-tests methods until postrun_teardown is removed. + if(_does_class_name_match(_inner_class_name, the_script.inner_class_name)): + _call_deprecated_script_method(test_script, 'postrun_teardown', 'after_all') + test_script.after_all() + + _log_test_children_warning(test_script) + # This might end up being very resource intensive if the scripts + # don't clean up after themselves. Might have to consolidate output + # into some other structure and kill the script objects with + # test_script.free() instead of remove child. + remove_child(test_script) + # END TESTS IN SCRIPT LOOP + + _lgr.set_indent_level(0) + if(test_script.get_assert_count() > 0): + var script_sum = str(test_script.get_pass_count(), '/', test_script.get_assert_count(), ' passed.') + _lgr.log(script_sum, _lgr.fmts.bold) + + _gui.set_progress_script_value(test_indexes + 1) # new way + # END TEST SCRIPT LOOP + + + _lgr.set_indent_level(0) + _end_run() + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _pass(text=''): + _gui.add_passing() # increments counters + if(_current_test): + _new_summary.add_pass(_current_test.name, text) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _fail(text=''): + _gui.add_failing() # increments counters + if(_current_test != null): + var line_text = ' at line ' + str(_extractLineNumber( _current_test)) + p(line_text, LOG_LEVEL_FAIL_ONLY) + # format for summary + line_text = "\n " + line_text + var call_count_text = '' + if(_parameter_handler != null): + call_count_text = str('(call #', _parameter_handler.get_call_count(), ') ') + _new_summary.add_fail(_current_test.name, call_count_text + text + line_text) + _current_test.passed = false + + +# ------------------------------------------------------------------------------ +# Extracts the line number from curren stacktrace by matching the test case name +# ------------------------------------------------------------------------------ +func _extractLineNumber(current_test): + var line_number = current_test.line_number + # if stack trace available than extraxt the test case line number + var stackTrace = get_stack() + if(stackTrace!=null): + for index in stackTrace.size(): + var line = stackTrace[index] + var function = line.get("function") + if function == current_test.name: + line_number = line.get("line") + return line_number + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _pending(text=''): + if(_current_test): + _new_summary.add_pending(_current_test.name, text) + +# ------------------------------------------------------------------------------ +# Gets all the files in a directory and all subdirectories if get_include_subdirectories +# is true. The files returned are all sorted by name. +# ------------------------------------------------------------------------------ +func _get_files(path, prefix, suffix): + var files = [] + var directories = [] + + var d = Directory.new() + d.open(path) + # true parameter tells list_dir_begin not to include "." and ".." directories. + d.list_dir_begin(true) + + # Traversing a directory is kinda odd. You have to start the process of listing + # the contents of a directory with list_dir_begin then use get_next until it + # returns an empty string. Then I guess you should end it. + var fs_item = d.get_next() + var full_path = '' + while(fs_item != ''): + full_path = path.plus_file(fs_item) + + #file_exists returns fasle for directories + if(d.file_exists(full_path)): + if(fs_item.begins_with(prefix) and fs_item.ends_with(suffix)): + files.append(full_path) + elif(get_include_subdirectories() and d.dir_exists(full_path)): + directories.append(full_path) + + fs_item = d.get_next() + d.list_dir_end() + + for dir in range(directories.size()): + var dir_files = _get_files(directories[dir], prefix, suffix) + for i in range(dir_files.size()): + files.append(dir_files[i]) + + files.sort() + return files +######################### +# +# public +# +######################### + +# ------------------------------------------------------------------------------ +# Conditionally prints the text to the console/results variable based on the +# current log level and what level is passed in. Whenever currently in a test, +# the text will be indented under the test. It can be further indented if +# desired. +# +# The first time output is generated when in a test, the test name will be +# printed. +# +# NOT_USED_ANYMORE was indent level. This was deprecated in 7.0.0. +# ------------------------------------------------------------------------------ +func p(text, level=0, NOT_USED_ANYMORE=-123): + if(NOT_USED_ANYMORE != -123): + _lgr.deprecated('gut.p no longer supports the optional 3rd parameter for indent_level parameter.') + var str_text = str(text) + + if(level <= _utils.nvl(_log_level, 0)): + _lgr.log(str_text) + +################ +# +# RUN TESTS/ADD SCRIPTS +# +################ +func get_minimum_size(): + return Vector2(810, 380) + +# ------------------------------------------------------------------------------ +# Runs all the scripts that were added using add_script +# ------------------------------------------------------------------------------ +func test_scripts(run_rest=false): + clear_text() + + if(_script_name != null and _script_name != ''): + var indexes = _get_indexes_matching_script_name(_script_name) + if(indexes == []): + _lgr.error('Could not find script matching ' + _script_name) + else: + _test_the_scripts(indexes) + else: + _test_the_scripts([]) + +# alias +func run_tests(run_rest=false): + test_scripts(run_rest) + +# ------------------------------------------------------------------------------ +# Runs a single script passed in. +# ------------------------------------------------------------------------------ +func test_script(script): + _test_collector.set_test_class_prefix(_inner_class_prefix) + _test_collector.clear() + _test_collector.add_script(script) + _test_the_scripts() + +# ------------------------------------------------------------------------------ +# Adds a script to be run when test_scripts called. +# ------------------------------------------------------------------------------ +func add_script(script): + if(!Engine.is_editor_hint()): + _test_collector.set_test_class_prefix(_inner_class_prefix) + _test_collector.add_script(script) + _add_scripts_to_gui() + +# ------------------------------------------------------------------------------ +# Add all scripts in the specified directory that start with the prefix and end +# with the suffix. Does not look in sub directories. Can be called multiple +# times. +# ------------------------------------------------------------------------------ +func add_directory(path, prefix=_file_prefix, suffix=_file_extension): + # check for '' b/c the calls to addin the exported directories 1-6 will pass + # '' if the field has not been populated. This will cause res:// to be + # processed which will include all files if include_subdirectories is true. + if(path == '' or path == null): + return + + var d = Directory.new() + if(!d.dir_exists(path)): + _lgr.error(str('The path [', path, '] does not exist.')) + else: + var files = _get_files(path, prefix, suffix) + for i in range(files.size()): + add_script(files[i]) + +# ------------------------------------------------------------------------------ +# This will try to find a script in the list of scripts to test that contains +# the specified script name. It does not have to be a full match. It will +# select the first matching occurrence so that this script will run when run_tests +# is called. Works the same as the select_this_one option of add_script. +# +# returns whether it found a match or not +# ------------------------------------------------------------------------------ +func select_script(script_name): + _script_name = script_name + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func export_tests(path=_export_path): + if(path == null): + _lgr.error('You must pass a path or set the export_path before calling export_tests') + else: + var result = _test_collector.export_tests(path) + if(result): + p(_test_collector.to_s()) + p("Exported to " + path) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func import_tests(path=_export_path): + if(!_utils.file_exists(path)): + _lgr.error(str('Cannot import tests: the path [', path, '] does not exist.')) + else: + _test_collector.clear() + var result = _test_collector.import_tests(path) + if(result): + p(_test_collector.to_s()) + p("Imported from " + path) + _add_scripts_to_gui() + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func import_tests_if_none_found(): + if(!_cancel_import and _test_collector.scripts.size() == 0): + import_tests() + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func export_if_tests_found(): + if(_test_collector.scripts.size() > 0): + export_tests() + +################ +# +# MISC +# +################ + +# ------------------------------------------------------------------------------ +# Maximize test runner window to fit the viewport. +# ------------------------------------------------------------------------------ +func set_should_maximize(should): + _should_maximize = should + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_should_maximize(): + return _should_maximize + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func maximize(): + _gui.maximize() + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func disable_strict_datatype_checks(should): + _disable_strict_datatype_checks = should + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func is_strict_datatype_checks_disabled(): + return _disable_strict_datatype_checks + +# ------------------------------------------------------------------------------ +# Pauses the test and waits for you to press a confirmation button. Useful when +# you want to watch a test play out onscreen or inspect results. +# ------------------------------------------------------------------------------ +func end_yielded_test(): + _lgr.deprecated('end_yielded_test is no longer necessary, you can remove it.') + +# ------------------------------------------------------------------------------ +# Clears the text of the text box. This resets all counters. +# ------------------------------------------------------------------------------ +func clear_text(): + _gui.clear_text() + update() + +# ------------------------------------------------------------------------------ +# Get the number of tests that were ran +# ------------------------------------------------------------------------------ +func get_test_count(): + return _new_summary.get_totals().tests + +# ------------------------------------------------------------------------------ +# Get the number of assertions that were made +# ------------------------------------------------------------------------------ +func get_assert_count(): + var t = _new_summary.get_totals() + return t.passing + t.failing + +# ------------------------------------------------------------------------------ +# Get the number of assertions that passed +# ------------------------------------------------------------------------------ +func get_pass_count(): + return _new_summary.get_totals().passing + +# ------------------------------------------------------------------------------ +# Get the number of assertions that failed +# ------------------------------------------------------------------------------ +func get_fail_count(): + return _new_summary.get_totals().failing + +# ------------------------------------------------------------------------------ +# Get the number of tests flagged as pending +# ------------------------------------------------------------------------------ +func get_pending_count(): + return _new_summary.get_totals().pending + +# ------------------------------------------------------------------------------ +# Get the results of all tests ran as text. This string is the same as is +# displayed in the text box, and similar to what is printed to the console. +# ------------------------------------------------------------------------------ +func get_result_text(): + return _log_text + +# ------------------------------------------------------------------------------ +# Set the log level. Use one of the various LOG_LEVEL_* constants. +# ------------------------------------------------------------------------------ +func set_log_level(level): + _log_level = max(level, 0) + + # Level 0 settings + _lgr.set_less_test_names(level == 0) + # Explicitly always enabled + _lgr.set_type_enabled(_lgr.types.normal, true) + _lgr.set_type_enabled(_lgr.types.error, true) + _lgr.set_type_enabled(_lgr.types.pending, true) + + # Level 1 types + _lgr.set_type_enabled(_lgr.types.warn, level > 0) + _lgr.set_type_enabled(_lgr.types.deprecated, level > 0) + + # Level 2 types + _lgr.set_type_enabled(_lgr.types.passed, level > 1) + _lgr.set_type_enabled(_lgr.types.info, level > 1) + _lgr.set_type_enabled(_lgr.types.debug, level > 1) + + if(!Engine.is_editor_hint()): + _gui.set_log_level(level) + +# ------------------------------------------------------------------------------ +# Get the current log level. +# ------------------------------------------------------------------------------ +func get_log_level(): + return _log_level + +# ------------------------------------------------------------------------------ +# Call this method to make the test pause before teardown so that you can inspect +# anything that you have rendered to the screen. +# ------------------------------------------------------------------------------ +func pause_before_teardown(): + _pause_before_teardown = true; + +# ------------------------------------------------------------------------------ +# For batch processing purposes, you may want to ignore any calls to +# pause_before_teardown that you forgot to remove. +# ------------------------------------------------------------------------------ +func set_ignore_pause_before_teardown(should_ignore): + _ignore_pause_before_teardown = should_ignore + _gui.set_ignore_pause(should_ignore) + +func get_ignore_pause_before_teardown(): + return _ignore_pause_before_teardown + +# ------------------------------------------------------------------------------ +# Set to true so that painting of the screen will occur between tests. Allows you +# to see the output as tests occur. Especially useful with long running tests that +# make it appear as though it has humg. +# +# NOTE: not compatible with 1.0 so this is disabled by default. This will +# change in future releases. +# ------------------------------------------------------------------------------ +func set_yield_between_tests(should): + _yield_between.should = should + +func get_yield_between_tests(): + return _yield_between.should + +# ------------------------------------------------------------------------------ +# Call _process or _fixed_process, if they exist, on obj and all it's children +# and their children and so and so forth. Delta will be passed through to all +# the _process or _fixed_process methods. +# ------------------------------------------------------------------------------ +func simulate(obj, times, delta): + for _i in range(times): + if(obj.has_method("_process")): + obj._process(delta) + if(obj.has_method("_physics_process")): + obj._physics_process(delta) + + for kid in obj.get_children(): + simulate(kid, 1, delta) + +# ------------------------------------------------------------------------------ +# Starts an internal timer with a timeout of the passed in time. A 'timeout' +# signal will be sent when the timer ends. Returns itself so that it can be +# used in a call to yield...cutting down on lines of code. +# +# Example, yield to the Gut object for 10 seconds: +# yield(gut.set_yield_time(10), 'timeout') +# ------------------------------------------------------------------------------ +func set_yield_time(time, text=''): + _yield_timer.set_wait_time(time) + _yield_timer.start() + var msg = '/# Yielding (' + str(time) + 's)' + if(text == ''): + msg += ' #/' + else: + msg += ': ' + text + ' #/' + _lgr.log(msg, _lgr.fmts.yellow) + _was_yield_method_called = true + return self + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_yield_signal_or_time(obj, signal_name, max_wait, text=''): + obj.connect(signal_name, self, '_yielding_callback', [true]) + _yielding_to.obj = obj + _yielding_to.signal_name = signal_name + + _yield_timer.set_wait_time(max_wait) + _yield_timer.start() + _was_yield_method_called = true + _lgr.log(str('/# Yielding to signal "', signal_name, '" or for ', max_wait, ' seconds #/ ', text), _lgr.fmts.yellow) + return self + +# ------------------------------------------------------------------------------ +# get the specific unit test that should be run +# ------------------------------------------------------------------------------ +func get_unit_test_name(): + return _unit_test_name + +# ------------------------------------------------------------------------------ +# set the specific unit test that should be run. +# ------------------------------------------------------------------------------ +func set_unit_test_name(test_name): + _unit_test_name = test_name + +# ------------------------------------------------------------------------------ +# Creates an empty file at the specified path +# ------------------------------------------------------------------------------ +func file_touch(path): + var f = File.new() + f.open(path, f.WRITE) + f.close() + +# ------------------------------------------------------------------------------ +# deletes the file at the specified path +# ------------------------------------------------------------------------------ +func file_delete(path): + var d = Directory.new() + var result = d.open(path.get_base_dir()) + if(result == OK): + d.remove(path) + +# ------------------------------------------------------------------------------ +# Checks to see if the passed in file has any data in it. +# ------------------------------------------------------------------------------ +func is_file_empty(path): + var f = File.new() + f.open(path, f.READ) + var empty = f.get_len() == 0 + f.close() + return empty + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_file_as_text(path): + return _utils.get_file_as_text(path) + +# ------------------------------------------------------------------------------ +# deletes all files in a given directory +# ------------------------------------------------------------------------------ +func directory_delete_files(path): + var d = Directory.new() + var result = d.open(path) + + # SHORTCIRCUIT + if(result != OK): + return + + # Traversing a directory is kinda odd. You have to start the process of listing + # the contents of a directory with list_dir_begin then use get_next until it + # returns an empty string. Then I guess you should end it. + d.list_dir_begin() + var thing = d.get_next() # could be a dir or a file or something else maybe? + var full_path = '' + while(thing != ''): + full_path = path + "/" + thing + #file_exists returns fasle for directories + if(d.file_exists(full_path)): + d.remove(full_path) + thing = d.get_next() + d.list_dir_end() + +# ------------------------------------------------------------------------------ +# Returns the instantiated script object that is currently being run. +# ------------------------------------------------------------------------------ +func get_current_script_object(): + var to_return = null + if(_test_script_objects.size() > 0): + to_return = _test_script_objects[-1] + return to_return + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_current_test_object(): + return _current_test + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_stubber(): + return _stubber + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_doubler(): + return _doubler + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_spy(): + return _spy + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_temp_directory(): + return _temp_directory + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_temp_directory(temp_directory): + _temp_directory = temp_directory + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_inner_class_name(): + return _inner_class_name + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_inner_class_name(inner_class_name): + _inner_class_name = inner_class_name + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_summary(): + return _new_summary + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_double_strategy(): + return _double_strategy + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_double_strategy(double_strategy): + _double_strategy = double_strategy + _doubler.set_strategy(double_strategy) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_include_subdirectories(): + return _include_subdirectories + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_logger(): + return _lgr + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_logger(logger): + _lgr = logger + _lgr.set_gut(self) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_include_subdirectories(include_subdirectories): + _include_subdirectories = include_subdirectories + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_test_collector(): + return _test_collector + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_export_path(): + return _export_path + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_export_path(export_path): + _export_path = export_path + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_version(): + return _utils.version + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_pre_run_script(): + return _pre_run_script + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_pre_run_script(pre_run_script): + _pre_run_script = pre_run_script + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_post_run_script(): + return _post_run_script + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_post_run_script(post_run_script): + _post_run_script = post_run_script + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_pre_run_script_instance(): + return _pre_run_script_instance + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_post_run_script_instance(): + return _post_run_script_instance + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_color_output(): + return _color_output + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_color_output(color_output): + _color_output = color_output + _lgr.disable_formatting(!color_output) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_parameter_handler(): + return _parameter_handler + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func set_parameter_handler(parameter_handler): + _parameter_handler = parameter_handler + _parameter_handler.set_logger(_lgr) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_gui(): + return _gui + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_orphan_counter(): + return _orphan_counter + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func show_orphans(should): + _lgr.set_type_enabled(_lgr.types.orphan, should) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func get_autofree(): + return _autofree diff --git a/test_proj/addons/gut/gut_cmdln.gd b/test_proj/addons/gut/gut_cmdln.gd new file mode 100644 index 0000000..6ff7c7c --- /dev/null +++ b/test_proj/addons/gut/gut_cmdln.gd @@ -0,0 +1,402 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# Description +# ----------- +# Command line interface for the GUT unit testing tool. Allows you to run tests +# from the command line instead of running a scene. Place this script along with +# gut.gd into your scripts directory at the root of your project. Once there you +# can run this script (from the root of your project) using the following command: +# godot -s -d test/gut/gut_cmdln.gd +# +# See the readme for a list of options and examples. You can also use the -gh +# option to get more information about how to use the command line interface. +# ############################################################################## +extends SceneTree + +var Optparse = load('res://addons/gut/optparse.gd') +var Gut = load('res://addons/gut/gut.gd') + +# ------------------------------------------------------------------------------ +# Helper class to resolve the various different places where an option can +# be set. Using the get_value method will enforce the order of precedence of: +# 1. command line value +# 2. config file value +# 3. default value +# +# The idea is that you set the base_opts. That will get you a copies of the +# hash with null values for the other types of values. Lower precedented hashes +# will punch through null values of higher precedented hashes. +# ------------------------------------------------------------------------------ +class OptionResolver: + var base_opts = null + var cmd_opts = null + var config_opts = null + + + func get_value(key): + return _nvl(cmd_opts[key], _nvl(config_opts[key], base_opts[key])) + + func set_base_opts(opts): + base_opts = opts + cmd_opts = _null_copy(opts) + config_opts = _null_copy(opts) + + # creates a copy of a hash with all values null. + func _null_copy(h): + var new_hash = {} + for key in h: + new_hash[key] = null + return new_hash + + func _nvl(a, b): + if(a == null): + return b + else: + return a + func _string_it(h): + var to_return = '' + for key in h: + to_return += str('(',key, ':', _nvl(h[key], 'NULL'), ')') + return to_return + + func to_s(): + return str("base:\n", _string_it(base_opts), "\n", \ + "config:\n", _string_it(config_opts), "\n", \ + "cmd:\n", _string_it(cmd_opts), "\n", \ + "resolved:\n", _string_it(get_resolved_values())) + + func get_resolved_values(): + var to_return = {} + for key in base_opts: + to_return[key] = get_value(key) + return to_return + + func to_s_verbose(): + var to_return = '' + var resolved = get_resolved_values() + for key in base_opts: + to_return += str(key, "\n") + to_return += str(' default: ', _nvl(base_opts[key], 'NULL'), "\n") + to_return += str(' config: ', _nvl(config_opts[key], ' --'), "\n") + to_return += str(' cmd: ', _nvl(cmd_opts[key], ' --'), "\n") + to_return += str(' final: ', _nvl(resolved[key], 'NULL'), "\n") + + return to_return + +# ------------------------------------------------------------------------------ +# Here starts the actual script that uses the Options class to kick off Gut +# and run your tests. +# ------------------------------------------------------------------------------ +var _utils = load('res://addons/gut/utils.gd').get_instance() +# instance of gut +var _tester = null +# array of command line options specified +var _final_opts = [] +# Hash for easier access to the options in the code. Options will be +# extracted into this hash and then the hash will be used afterwards so +# that I don't make any dumb typos and get the neat code-sense when I +# type a dot. +var options = { + background_color = Color(.15, .15, .15, 1).to_html(), + config_file = 'res://.gutconfig.json', + dirs = [], + disable_colors = false, + double_strategy = 'partial', + font_color = Color(.8, .8, .8, 1).to_html(), + font_name = 'CourierPrime', + font_size = 16, + hide_orphans = false, + ignore_pause = false, + include_subdirs = false, + inner_class = '', + log_level = 1, + opacity = 100, + post_run_script = '', + pre_run_script = '', + prefix = 'test_', + selected = '', + should_exit = false, + should_exit_on_success = false, + should_maximize = false, + show_help = false, + suffix = '.gd', + tests = [], + unit_test_name = '', +} +var valid_fonts = ['AnonymousPro', 'CourierPro', 'LobsterTwo', 'Default'] + +# flag to indicate if only a single script should be run. +var _run_single = false + + +func setup_options(): + var opts = Optparse.new() + opts.set_banner(('This is the command line interface for the unit testing tool Gut. With this ' + + 'interface you can run one or more test scripts from the command line. In order ' + + 'for the Gut options to not clash with any other godot options, each option starts ' + + 'with a "g". Also, any option that requires a value will take the form of ' + + '"-g=". There cannot be any spaces between the option, the "=", or ' + + 'inside a specified value or godot will think you are trying to run a scene.')) + opts.add('-gtest', [], 'Comma delimited list of full paths to test scripts to run.') + opts.add('-gdir', options.dirs, 'Comma delimited list of directories to add tests from.') + opts.add('-gprefix', options.prefix, 'Prefix used to find tests when specifying -gdir. Default "[default]".') + opts.add('-gsuffix', options.suffix, 'Suffix used to find tests when specifying -gdir. Default "[default]".') + opts.add('-ghide_orphans', false, 'Display orphan counts for tests and scripts. Default "[default]".') + opts.add('-gmaximize', false, 'Maximizes test runner window to fit the viewport.') + opts.add('-gexit', false, 'Exit after running tests. If not specified you have to manually close the window.') + opts.add('-gexit_on_success', false, 'Only exit if all tests pass.') + opts.add('-glog', options.log_level, 'Log level. Default [default]') + opts.add('-gignore_pause', false, 'Ignores any calls to gut.pause_before_teardown.') + opts.add('-gselect', '', ('Select a script to run initially. The first script that ' + + 'was loaded using -gtest or -gdir that contains the specified ' + + 'string will be executed. You may run others by interacting ' + + 'with the GUI.')) + opts.add('-gunit_test_name', '', ('Name of a test to run. Any test that contains the specified ' + + 'text will be run, all others will be skipped.')) + opts.add('-gh', false, 'Print this help, then quit') + opts.add('-gconfig', 'res://.gutconfig.json', 'A config file that contains configuration information. Default is res://.gutconfig.json') + opts.add('-ginner_class', '', 'Only run inner classes that contain this string') + opts.add('-gopacity', options.opacity, 'Set opacity of test runner window. Use range 0 - 100. 0 = transparent, 100 = opaque.') + opts.add('-gpo', false, 'Print option values from all sources and the value used, then quit.') + opts.add('-ginclude_subdirs', false, 'Include subdirectories of -gdir.') + opts.add('-gdouble_strategy', 'partial', 'Default strategy to use when doubling. Valid values are [partial, full]. Default "[default]"') + opts.add('-gdisable_colors', false, 'Disable command line colors.') + opts.add('-gpre_run_script', '', 'pre-run hook script path') + opts.add('-gpost_run_script', '', 'post-run hook script path') + opts.add('-gprint_gutconfig_sample', false, 'Print out json that can be used to make a gutconfig file then quit.') + + opts.add('-gfont_name', options.font_name, str('Valid values are: ', valid_fonts, '. Default "[default]"')) + opts.add('-gfont_size', options.font_size, 'Font size, default "[default]"') + opts.add('-gbackground_color', options.background_color, 'Background color as an html color, default "[default]"') + opts.add('-gfont_color',options.font_color, 'Font color as an html color, default "[default]"') + return opts + + +# Parses options, applying them to the _tester or setting values +# in the options struct. +func extract_command_line_options(from, to): + to.config_file = from.get_value('-gconfig') + to.dirs = from.get_value('-gdir') + to.disable_colors = from.get_value('-gdisable_colors') + to.double_strategy = from.get_value('-gdouble_strategy') + to.ignore_pause = from.get_value('-gignore_pause') + to.include_subdirs = from.get_value('-ginclude_subdirs') + to.inner_class = from.get_value('-ginner_class') + to.log_level = from.get_value('-glog') + to.opacity = from.get_value('-gopacity') + to.post_run_script = from.get_value('-gpost_run_script') + to.pre_run_script = from.get_value('-gpre_run_script') + to.prefix = from.get_value('-gprefix') + to.selected = from.get_value('-gselect') + to.should_exit = from.get_value('-gexit') + to.should_exit_on_success = from.get_value('-gexit_on_success') + to.should_maximize = from.get_value('-gmaximize') + to.hide_orphans = from.get_value('-ghide_orphans') + to.suffix = from.get_value('-gsuffix') + to.tests = from.get_value('-gtest') + to.unit_test_name = from.get_value('-gunit_test_name') + + to.font_size = from.get_value('-gfont_size') + to.font_name = from.get_value('-gfont_name') + to.background_color = from.get_value('-gbackground_color') + to.font_color = from.get_value('-gfont_color') + + +func load_options_from_config_file(file_path, into): + # SHORTCIRCUIT + var f = File.new() + if(!f.file_exists(file_path)): + if(file_path != 'res://.gutconfig.json'): + print('ERROR: Config File "', file_path, '" does not exist.') + return -1 + else: + return 1 + + f.open(file_path, f.READ) + var json = f.get_as_text() + f.close() + + var results = JSON.parse(json) + # SHORTCIRCUIT + if(results.error != OK): + print("\n\n",'!! ERROR parsing file: ', file_path) + print(' at line ', results.error_line, ':') + print(' ', results.error_string) + return -1 + + # Get all the options out of the config file using the option name. The + # options hash is now the default source of truth for the name of an option. + for key in into: + if(results.result.has(key)): + into[key] = results.result[key] + + return 1 + + +# Apply all the options specified to _tester. This is where the rubber meets +# the road. +func apply_options(opts): + _tester = Gut.new() + get_root().add_child(_tester) + _tester.connect('tests_finished', self, '_on_tests_finished', [opts.should_exit, opts.should_exit_on_success]) + _tester.set_yield_between_tests(true) + _tester.set_modulate(Color(1.0, 1.0, 1.0, min(1.0, float(opts.opacity) / 100))) + _tester.show() + + _tester.set_include_subdirectories(opts.include_subdirs) + + if(opts.should_maximize): + _tester.maximize() + + if(opts.inner_class != ''): + _tester.set_inner_class_name(opts.inner_class) + _tester.set_log_level(opts.log_level) + _tester.set_ignore_pause_before_teardown(opts.ignore_pause) + + for i in range(opts.dirs.size()): + _tester.add_directory(opts.dirs[i], opts.prefix, opts.suffix) + + for i in range(opts.tests.size()): + _tester.add_script(opts.tests[i]) + + if(opts.selected != ''): + _tester.select_script(opts.selected) + _run_single = true + + if(opts.double_strategy == 'full'): + _tester.set_double_strategy(_utils.DOUBLE_STRATEGY.FULL) + elif(opts.double_strategy == 'partial'): + _tester.set_double_strategy(_utils.DOUBLE_STRATEGY.PARTIAL) + + _tester.set_unit_test_name(opts.unit_test_name) + _tester.set_pre_run_script(opts.pre_run_script) + _tester.set_post_run_script(opts.post_run_script) + _tester.set_color_output(!opts.disable_colors) + _tester.show_orphans(!opts.hide_orphans) + + _tester.get_gui().set_font_size(opts.font_size) + _tester.get_gui().set_font(opts.font_name) + if(opts.font_color != null and opts.font_color.is_valid_html_color()): + _tester.get_gui().set_default_font_color(Color(opts.font_color)) + if(opts.background_color != null and opts.background_color.is_valid_html_color()): + _tester.get_gui().set_background_color(Color(opts.background_color)) + + +func _print_gutconfigs(values): + var header = """Here is a sample of a full .gutconfig.json file. +You do not need to specify all values in your own file. The values supplied in +this sample are what would be used if you ran gut w/o the -gprint_gutconfig_sample +option (the resolved values where default < .gutconfig < command line).""" + print("\n", header.replace("\n", ' '), "\n\n") + var resolved = values + + # remove some options that don't make sense to be in config + resolved.erase("config_file") + resolved.erase("show_help") + + print("Here's a config with all the properties set based off of your current command and config.") + var text = JSON.print(resolved) + print(text.replace(',', ",\n")) + + for key in resolved: + resolved[key] = null + + print("\n\nAnd here's an empty config for you fill in what you want.") + text = JSON.print(resolved) + print(text.replace(',', ",\n")) + + +# parse options and run Gut +func _run_gut(): + var opt_resolver = OptionResolver.new() + opt_resolver.set_base_opts(options) + + print("\n\n", ' --- Gut ---') + var o = setup_options() + + var all_options_valid = o.parse() + extract_command_line_options(o, opt_resolver.cmd_opts) + var load_result = \ + load_options_from_config_file(opt_resolver.get_value('config_file'), opt_resolver.config_opts) + + if(load_result == -1): # -1 indicates json parse error + quit() + else: + if(!all_options_valid): + quit() + elif(o.get_value('-gh')): + print(_utils.get_version_text()) + o.print_help() + quit() + elif(o.get_value('-gpo')): + print('All command line options and where they are specified. ' + + 'The "final" value shows which value will actually be used ' + + 'based on order of precedence (default < .gutconfig < cmd line).' + "\n") + print(opt_resolver.to_s_verbose()) + quit() + elif(o.get_value('-gprint_gutconfig_sample')): + _print_gutconfigs(opt_resolver.get_resolved_values()) + quit() + else: + _final_opts = opt_resolver.get_resolved_values(); + apply_options(_final_opts) + _tester.test_scripts(!_run_single) + + +# exit if option is set. +func _on_tests_finished(should_exit, should_exit_on_success): + if(_final_opts.dirs.size() == 0): + if(_tester.get_summary().get_totals().scripts == 0): + var lgr = _tester.get_logger() + lgr.error('No directories configured. Add directories with options or a .gutconfig.json file. Use the -gh option for more information.') + + if(_tester.get_fail_count()): + OS.exit_code = 1 + + # Overwrite the exit code with the post_script + var post_inst = _tester.get_post_run_script_instance() + if(post_inst != null and post_inst.get_exit_code() != null): + OS.exit_code = post_inst.get_exit_code() + + if(should_exit or (should_exit_on_success and _tester.get_fail_count() == 0)): + quit() + else: + print("Tests finished, exit manually") + +# ------------------------------------------------------------------------------ +# MAIN +# ------------------------------------------------------------------------------ +func _init(): + if(!_utils.is_version_ok()): + print("\n\n", _utils.get_version_text()) + push_error(_utils.get_bad_version_text()) + OS.exit_code = 1 + quit() + else: + _run_gut() diff --git a/test_proj/addons/gut/gut_plugin.gd b/test_proj/addons/gut/gut_plugin.gd new file mode 100644 index 0000000..8981f28 --- /dev/null +++ b/test_proj/addons/gut/gut_plugin.gd @@ -0,0 +1,12 @@ +tool +extends EditorPlugin + +func _enter_tree(): + # Initialization of the plugin goes here + # Add the new type with a name, a parent type, a script and an icon + add_custom_type("Gut", "Control", preload("plugin_control.gd"), preload("icon.png")) + +func _exit_tree(): + # Clean-up of the plugin goes here + # Always remember to remove it from the engine when deactivated + remove_custom_type("Gut") diff --git a/test_proj/addons/gut/hook_script.gd b/test_proj/addons/gut/hook_script.gd new file mode 100644 index 0000000..10a5e12 --- /dev/null +++ b/test_proj/addons/gut/hook_script.gd @@ -0,0 +1,35 @@ +# ------------------------------------------------------------------------------ +# This script is the base for custom scripts to be used in pre and post +# run hooks. +# ------------------------------------------------------------------------------ + +# This is the instance of GUT that is running the tests. You can get +# information about the run from this object. This is set by GUT when the +# script is instantiated. +var gut = null + +# the exit code to be used by gut_cmdln. See set method. +var _exit_code = null + +var _should_abort = false +# Virtual method that will be called by GUT after instantiating +# this script. +func run(): + pass + +# Set the exit code when running from the command line. If not set then the +# default exit code will be returned (0 when no tests fail, 1 when any tests +# fail). +func set_exit_code(code): + _exit_code = code + +func get_exit_code(): + return _exit_code + +# Usable by pre-run script to cause the run to end AFTER the run() method +# finishes. post-run script will not be ran. +func abort(): + _should_abort = true + +func should_abort(): + return _should_abort diff --git a/test_proj/addons/gut/icon.png b/test_proj/addons/gut/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7c589874e72ccdeae8dc1a45907fba0d63e603cf GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8Uop6XFV_m%d*!21sKVXw&;xMaGo1Os_Qt@2OC7#SFv=^B{n8kvR|7+D!uSecq=gBS*JG9l+sH00)|WTsW()}ZhBrU+z( Mr>mdKI;Vst04A4Hj{pDw literal 0 HcmV?d00001 diff --git a/test_proj/addons/gut/icon.png.import b/test_proj/addons/gut/icon.png.import new file mode 100644 index 0000000..848473e --- /dev/null +++ b/test_proj/addons/gut/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-91b084043b8aaf2f1c906e7b9fa92969.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/gut/icon.png" +dest_files=[ "res://.import/icon.png-91b084043b8aaf2f1c906e7b9fa92969.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/test_proj/addons/gut/logger.gd b/test_proj/addons/gut/logger.gd new file mode 100644 index 0000000..900b928 --- /dev/null +++ b/test_proj/addons/gut/logger.gd @@ -0,0 +1,348 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# This class wraps around the various printers and supplies formatting for the +# various message types (error, warning, etc). +# ############################################################################## +var types = { + debug = 'debug', + deprecated = 'deprecated', + error = 'error', + failed = 'failed', + info = 'info', + normal = 'normal', + orphan = 'orphan', + passed = 'passed', + pending = 'pending', + warn ='warn', +} + +var fmts = { + red = 'red', + yellow = 'yellow', + green = 'green', + + bold = 'bold', + underline = 'underline', + + none = null +} + +var _type_data = { + types.debug: {disp='DEBUG', enabled=true, fmt=fmts.none}, + types.deprecated: {disp='DEPRECATED', enabled=true, fmt=fmts.none}, + types.error: {disp='ERROR', enabled=true, fmt=fmts.red}, + types.failed: {disp='Failed', enabled=true, fmt=fmts.red}, + types.info: {disp='INFO', enabled=true, fmt=fmts.bold}, + types.normal: {disp='NORMAL', enabled=true, fmt=fmts.none}, + types.orphan: {disp='Orphans', enabled=true, fmt=fmts.yellow}, + types.passed: {disp='Passed', enabled=true, fmt=fmts.green}, + types.pending: {disp='Pending', enabled=true, fmt=fmts.yellow}, + types.warn: {disp='WARNING', enabled=true, fmt=fmts.yellow}, +} + +var _logs = { + types.warn: [], + types.error: [], + types.info: [], + types.debug: [], + types.deprecated: [], +} + +var _printers = { + terminal = null, + gui = null, + console = null +} + +var _gut = null +var _utils = null +var _indent_level = 0 +var _indent_string = ' ' +var _skip_test_name_for_testing = false +var _less_test_names = false +var _yield_calls = 0 +var _last_yield_text = '' + + +func _init(): + _utils = load('res://addons/gut/utils.gd').get_instance() + _printers.terminal = _utils.Printers.TerminalPrinter.new() + _printers.console = _utils.Printers.ConsolePrinter.new() + # There were some problems in the timing of disabling this at the right + # time in gut_cmdln so it is disabled by default. This is enabled + # by plugin_control.gd based on settings. + _printers.console.set_disabled(true) + +func get_indent_text(): + var pad = '' + for i in range(_indent_level): + pad += _indent_string + + return pad + +func _indent_text(text): + var to_return = text + var ending_newline = '' + + if(text.ends_with("\n")): + ending_newline = "\n" + to_return = to_return.left(to_return.length() -1) + + var pad = get_indent_text() + to_return = to_return.replace("\n", "\n" + pad) + to_return += ending_newline + + return pad + to_return + +func _should_print_to_printer(key_name): + return _printers[key_name] != null and !_printers[key_name].get_disabled() + +func _print_test_name(): + if(_gut == null): + return + var cur_test = _gut.get_current_test_object() + if(cur_test == null): + return false + + if(!cur_test.has_printed_name): + _output('* ' + cur_test.name + "\n") + cur_test.has_printed_name = true + +func _output(text, fmt=null): + for key in _printers: + if(_should_print_to_printer(key)): + var info = ''#str(self, ':', key, ':', _printers[key], '| ') + _printers[key].send(info + text, fmt) + +func _log(text, fmt=fmts.none): + _print_test_name() + var indented = _indent_text(text) + _output(indented, fmt) + +# --------------- +# Get Methods +# --------------- +func get_warnings(): + return get_log_entries(types.warn) + +func get_errors(): + return get_log_entries(types.error) + +func get_infos(): + return get_log_entries(types.info) + +func get_debugs(): + return get_log_entries(types.debug) + +func get_deprecated(): + return get_log_entries(types.deprecated) + +func get_count(log_type=null): + var count = 0 + if(log_type == null): + for key in _logs: + count += _logs[key].size() + else: + count = _logs[log_type].size() + return count + +func get_log_entries(log_type): + return _logs[log_type] + +# --------------- +# Log methods +# --------------- +func _output_type(type, text): + var td = _type_data[type] + if(!td.enabled): + return + + _print_test_name() + if(type != types.normal): + if(_logs.has(type)): + _logs[type].append(text) + + var start = str('[', td.disp, ']') + if(text != null and text != ''): + start += ': ' + else: + start += ' ' + var indented_start = _indent_text(start) + var indented_end = _indent_text(text) + indented_end = indented_end.lstrip(_indent_string) + _output(indented_start, td.fmt) + _output(indented_end + "\n") + +func debug(text): + _output_type(types.debug, text) + +# supply some text or the name of the deprecated method and the replacement. +func deprecated(text, alt_method=null): + var msg = text + if(alt_method): + msg = str('The method ', text, ' is deprecated, use ', alt_method , ' instead.') + return _output_type(types.deprecated, msg) + +func error(text): + _output_type(types.error, text) + +func failed(text): + _output_type(types.failed, text) + +func info(text): + _output_type(types.info, text) + +func orphan(text): + _output_type(types.orphan, text) + +func passed(text): + _output_type(types.passed, text) + +func pending(text): + _output_type(types.pending, text) + +func warn(text): + _output_type(types.warn, text) + +func log(text='', fmt=fmts.none): + end_yield() + if(text == ''): + _output("\n") + else: + _log(text + "\n", fmt) + return null + +func lograw(text, fmt=fmts.none): + return _output(text, fmt) + +# Print the test name if we aren't skipping names of tests that pass (basically +# what _less_test_names means)) +func log_test_name(): + # suppress output if we haven't printed the test name yet and + # what to print is the test name. + if(!_less_test_names): + _print_test_name() + +# --------------- +# Misc +# --------------- +func get_gut(): + return _gut + +func set_gut(gut): + _gut = gut + if(_gut == null): + _printers.gui = null + else: + if(_printers.gui == null): + _printers.gui = _utils.Printers.GutGuiPrinter.new() + _printers.gui.set_gut(gut) + +func get_indent_level(): + return _indent_level + +func set_indent_level(indent_level): + _indent_level = indent_level + +func get_indent_string(): + return _indent_string + +func set_indent_string(indent_string): + _indent_string = indent_string + +func clear(): + for key in _logs: + _logs[key].clear() + +func inc_indent(): + _indent_level += 1 + +func dec_indent(): + _indent_level = max(0, _indent_level -1) + +func is_type_enabled(type): + return _type_data[type].enabled + +func set_type_enabled(type, is_enabled): + _type_data[type].enabled = is_enabled + +func get_less_test_names(): + return _less_test_names + +func set_less_test_names(less_test_names): + _less_test_names = less_test_names + +func disable_printer(name, is_disabled): + _printers[name].set_disabled(is_disabled) + +func is_printer_disabled(name): + return _printers[name].get_disabled() + +func disable_formatting(is_disabled): + for key in _printers: + _printers[key].set_format_enabled(!is_disabled) + +func get_printer(printer_key): + return _printers[printer_key] + +func _yield_text_terminal(text): + var printer = _printers['terminal'] + if(_yield_calls != 0): + printer.clear_line() + printer.back(_last_yield_text.length()) + printer.send(text, fmts.yellow) + +func _end_yield_terminal(): + var printer = _printers['terminal'] + printer.clear_line() + printer.back(_last_yield_text.length()) + +func _yield_text_gui(text): + var lbl = _gut.get_gui().get_waiting_label() + lbl.visible = true + lbl.set_bbcode('[color=yellow]' + text + '[/color]') + +func _end_yield_gui(): + var lbl = _gut.get_gui().get_waiting_label() + lbl.visible = false + lbl.set_text('') + +func yield_text(text): + _yield_text_terminal(text) + _yield_text_gui(text) + _last_yield_text = text + _yield_calls += 1 + +func end_yield(): + if(_yield_calls == 0): + return + _end_yield_terminal() + _end_yield_gui() + _yield_calls = 0 + _last_yield_text = '' diff --git a/test_proj/addons/gut/method_maker.gd b/test_proj/addons/gut/method_maker.gd new file mode 100644 index 0000000..0415b88 --- /dev/null +++ b/test_proj/addons/gut/method_maker.gd @@ -0,0 +1,213 @@ +# This class will generate method declaration lines based on method meta +# data. It will create defaults that match the method data. +# +# -------------------- +# function meta data +# -------------------- +# name: +# flags: +# args: [{ +# (class_name:), +# (hint:0), +# (hint_string:), +# (name:), +# (type:4), +# (usage:7) +# }] +# default_args [] + +var _utils = load('res://addons/gut/utils.gd').get_instance() +var _lgr = _utils.get_logger() +const PARAM_PREFIX = 'p_' + +# ------------------------------------------------------ +# _supported_defaults +# +# This array contains all the data types that are supported for default values. +# If a value is supported it will contain either an empty string or a prefix +# that should be used when setting the parameter default value. +# For example int, real, bool do not need anything func(p1=1, p2=2.2, p3=false) +# but things like Vectors and Colors do since only the parameters to create a +# new Vector or Color are included in the metadata. +# ------------------------------------------------------ + # TYPE_NIL = 0 — Variable is of type nil (only applied for null). + # TYPE_BOOL = 1 — Variable is of type bool. + # TYPE_INT = 2 — Variable is of type int. + # TYPE_REAL = 3 — Variable is of type float/real. + # TYPE_STRING = 4 — Variable is of type String. + # TYPE_VECTOR2 = 5 — Variable is of type Vector2. + # TYPE_RECT2 = 6 — Variable is of type Rect2. + # TYPE_VECTOR3 = 7 — Variable is of type Vector3. + # TYPE_COLOR = 14 — Variable is of type Color. + # TYPE_OBJECT = 17 — Variable is of type Object. + # TYPE_DICTIONARY = 18 — Variable is of type Dictionary. + # TYPE_ARRAY = 19 — Variable is of type Array. + # TYPE_VECTOR2_ARRAY = 24 — Variable is of type PoolVector2Array. + + + +# TYPE_TRANSFORM2D = 8 — Variable is of type Transform2D. +# TYPE_PLANE = 9 — Variable is of type Plane. +# TYPE_QUAT = 10 — Variable is of type Quat. +# TYPE_AABB = 11 — Variable is of type AABB. +# TYPE_BASIS = 12 — Variable is of type Basis. +# TYPE_TRANSFORM = 13 — Variable is of type Transform. +# TYPE_NODE_PATH = 15 — Variable is of type NodePath. +# TYPE_RID = 16 — Variable is of type RID. +# TYPE_RAW_ARRAY = 20 — Variable is of type PoolByteArray. +# TYPE_INT_ARRAY = 21 — Variable is of type PoolIntArray. +# TYPE_REAL_ARRAY = 22 — Variable is of type PoolRealArray. +# TYPE_STRING_ARRAY = 23 — Variable is of type PoolStringArray. +# TYPE_VECTOR3_ARRAY = 25 — Variable is of type PoolVector3Array. +# TYPE_COLOR_ARRAY = 26 — Variable is of type PoolColorArray. +# TYPE_MAX = 27 — Marker for end of type constants. +# ------------------------------------------------------ +var _supported_defaults = [] + +func _init(): + for _i in range(TYPE_MAX): + _supported_defaults.append(null) + + # These types do not require a prefix for defaults + _supported_defaults[TYPE_NIL] = '' + _supported_defaults[TYPE_BOOL] = '' + _supported_defaults[TYPE_INT] = '' + _supported_defaults[TYPE_REAL] = '' + _supported_defaults[TYPE_OBJECT] = '' + _supported_defaults[TYPE_ARRAY] = '' + _supported_defaults[TYPE_STRING] = '' + _supported_defaults[TYPE_DICTIONARY] = '' + _supported_defaults[TYPE_VECTOR2_ARRAY] = '' + + # These require a prefix for whatever default is provided + _supported_defaults[TYPE_VECTOR2] = 'Vector2' + _supported_defaults[TYPE_RECT2] = 'Rect2' + _supported_defaults[TYPE_VECTOR3] = 'Vector3' + _supported_defaults[TYPE_COLOR] = 'Color' + +# ############### +# Private +# ############### +var _func_text = _utils.get_file_as_text('res://addons/gut/double_templates/function_template.txt') + +func _is_supported_default(type_flag): + return type_flag >= 0 and type_flag < _supported_defaults.size() and [type_flag] != null + +# Creates a list of parameters with defaults of null unless a default value is +# found in the metadata. If a default is found in the meta then it is used if +# it is one we know how support. +# +# If a default is found that we don't know how to handle then this method will +# return null. +func _get_arg_text(method_meta): + var text = '' + var args = method_meta.args + var defaults = [] + var has_unsupported_defaults = false + + # fill up the defaults with null defaults for everything that doesn't have + # a default in the meta data. default_args is an array of default values + # for the last n parameters where n is the size of default_args so we only + # add nulls for everything up to the first parameter with a default. + for _i in range(args.size() - method_meta.default_args.size()): + defaults.append('null') + + # Add meta-data defaults. + for i in range(method_meta.default_args.size()): + var t = args[defaults.size()]['type'] + var value = '' + if(_is_supported_default(t)): + # strings are special, they need quotes around the value + if(t == TYPE_STRING): + value = str("'", str(method_meta.default_args[i]), "'") + # Colors need the parens but things like Vector2 and Rect2 don't + elif(t == TYPE_COLOR): + value = str(_supported_defaults[t], '(', str(method_meta.default_args[i]), ')') + elif(t == TYPE_OBJECT): + if(str(method_meta.default_args[i]) == "[Object:null]"): + value = str(_supported_defaults[t], 'null') + else: + value = str(_supported_defaults[t], str(method_meta.default_args[i]).to_lower()) + + # Everything else puts the prefix (if one is there) form _supported_defaults + # in front. The to_lower is used b/c for some reason the defaults for + # null, true, false are all "Null", "True", "False". + else: + value = str(_supported_defaults[t], str(method_meta.default_args[i]).to_lower()) + else: + _lgr.warn(str( + 'Unsupported default param type: ',method_meta.name, '-', args[defaults.size()].name, ' ', t, ' = ', method_meta.default_args[i])) + value = str('unsupported=',t) + has_unsupported_defaults = true + + defaults.append(value) + + # construct the string of parameters + for i in range(args.size()): + text += str(PARAM_PREFIX, args[i].name, '=', defaults[i]) + if(i != args.size() -1): + text += ', ' + + # if we don't know how to make a default then we have to return null b/c + # it will cause a runtime error and it's one thing we could return to let + # callers know it didn't work. + if(has_unsupported_defaults): + text = null + + return text + +# ############### +# Public +# ############### + +# Creates a delceration for a function based off of function metadata. All +# types whose defaults are supported will have their values. If a datatype +# is not supported and the parameter has a default, a warning message will be +# printed and the declaration will return null. +func get_function_text(meta): + var method_params = _get_arg_text(meta) + var text = null + + var param_array = get_spy_call_parameters_text(meta) + if(param_array == 'null'): + param_array = '[]' + + if(method_params != null): + var decleration = str('func ', meta.name, '(', method_params, '):') + text = _func_text.format({ + "func_decleration":decleration, + "method_name":meta.name, + "param_array":param_array, + "super_call":get_super_call_text(meta) + }) + return text + +# creates a call to the function in meta in the super's class. +func get_super_call_text(meta): + var params = '' + + for i in range(meta.args.size()): + params += PARAM_PREFIX + meta.args[i].name + if(meta.args.size() > 1 and i != meta.args.size() -1): + params += ', ' + if(meta.name == '_init'): + return 'null' + else: + return str('.', meta.name, '(', params, ')') + +func get_spy_call_parameters_text(meta): + var called_with = 'null' + if(meta.args.size() > 0): + called_with = '[' + for i in range(meta.args.size()): + called_with += str(PARAM_PREFIX, meta.args[i].name) + if(i < meta.args.size() - 1): + called_with += ', ' + called_with += ']' + return called_with + +func get_logger(): + return _lgr + +func set_logger(logger): + _lgr = logger diff --git a/test_proj/addons/gut/one_to_many.gd b/test_proj/addons/gut/one_to_many.gd new file mode 100644 index 0000000..6a0f818 --- /dev/null +++ b/test_proj/addons/gut/one_to_many.gd @@ -0,0 +1,38 @@ +# ------------------------------------------------------------------------------ +# This datastructure represents a simple one-to-many relationship. It manages +# a dictionary of value/array pairs. It ignores duplicates of both the "one" +# and the "many". +# ------------------------------------------------------------------------------ +var _items = {} + +# return the size of _items or the size of an element in _items if "one" was +# specified. +func size(one=null): + var to_return = 0 + if(one == null): + to_return = _items.size() + elif(_items.has(one)): + to_return = _items[one].size() + return to_return + +# Add an element to "one" if it does not already exist +func add(one, many_item): + if(_items.has(one) and !_items[one].has(many_item)): + _items[one].append(many_item) + else: + _items[one] = [many_item] + +func clear(): + _items.clear() + +func has(one, many_item): + var to_return = false + if(_items.has(one)): + to_return = _items[one].has(many_item) + return to_return + +func to_s(): + var to_return = '' + for key in _items: + to_return += str(key, ": ", _items[key], "\n") + return to_return diff --git a/test_proj/addons/gut/optparse.gd b/test_proj/addons/gut/optparse.gd new file mode 100644 index 0000000..290ed9d --- /dev/null +++ b/test_proj/addons/gut/optparse.gd @@ -0,0 +1,248 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# Description +# ----------- +# Command line interface for the GUT unit testing tool. Allows you to run tests +# from the command line instead of running a scene. Place this script along with +# gut.gd into your scripts directory at the root of your project. Once there you +# can run this script (from the root of your project) using the following command: +# godot -s -d test/gut/gut_cmdln.gd +# +# See the readme for a list of options and examples. You can also use the -gh +# option to get more information about how to use the command line interface. +# ############################################################################## + +#------------------------------------------------------------------------------- +# Parses the command line arguments supplied into an array that can then be +# examined and parsed based on how the gut options work. +#------------------------------------------------------------------------------- +class CmdLineParser: + var _used_options = [] + # an array of arrays. Each element in this array will contain an option + # name and if that option contains a value then it will have a sedond + # element. For example: + # [[-gselect, test.gd], [-gexit]] + var _opts = [] + + func _init(): + for i in range(OS.get_cmdline_args().size()): + var opt_val = OS.get_cmdline_args()[i].split('=') + _opts.append(opt_val) + + # Parse out multiple comma delimited values from a command line + # option. Values are separated from option name with "=" and + # additional values are comma separated. + func _parse_array_value(full_option): + var value = _parse_option_value(full_option) + var split = value.split(',') + return split + + # Parse out the value of an option. Values are separated from + # the option name with "=" + func _parse_option_value(full_option): + if(full_option.size() > 1): + return full_option[1] + else: + return null + + # Search _opts for an element that starts with the option name + # specified. + func find_option(name): + var found = false + var idx = 0 + + while(idx < _opts.size() and !found): + if(_opts[idx][0] == name): + found = true + else: + idx += 1 + + if(found): + return idx + else: + return -1 + + func get_array_value(option): + _used_options.append(option) + var to_return = [] + var opt_loc = find_option(option) + if(opt_loc != -1): + to_return = _parse_array_value(_opts[opt_loc]) + _opts.remove(opt_loc) + + return to_return + + # returns the value of an option if it was specified, null otherwise. This + # used to return the default but that became problemnatic when trying to + # punch through the different places where values could be specified. + func get_value(option): + _used_options.append(option) + var to_return = null + var opt_loc = find_option(option) + if(opt_loc != -1): + to_return = _parse_option_value(_opts[opt_loc]) + _opts.remove(opt_loc) + + return to_return + + # returns true if it finds the option, false if not. + func was_specified(option): + _used_options.append(option) + return find_option(option) != -1 + + # Returns any unused command line options. I found that only the -s and + # script name come through from godot, all other options that godot uses + # are not sent through OS.get_cmdline_args(). + # + # This is a onetime thing b/c i kill all items in _used_options + func get_unused_options(): + var to_return = [] + for i in range(_opts.size()): + to_return.append(_opts[i][0]) + + var script_option = to_return.find('-s') + if script_option != -1: + to_return.remove(script_option + 1) + to_return.remove(script_option) + + while(_used_options.size() > 0): + var index = to_return.find(_used_options[0].split("=")[0]) + if(index != -1): + to_return.remove(index) + _used_options.remove(0) + + return to_return + +#------------------------------------------------------------------------------- +# Simple class to hold a command line option +#------------------------------------------------------------------------------- +class Option: + var value = null + var option_name = '' + var default = null + var description = '' + + func _init(name, default_value, desc=''): + option_name = name + default = default_value + description = desc + value = null#default_value + + func pad(to_pad, size, pad_with=' '): + var to_return = to_pad + for _i in range(to_pad.length(), size): + to_return += pad_with + + return to_return + + func to_s(min_space=0): + var subbed_desc = description + if(subbed_desc.find('[default]') != -1): + subbed_desc = subbed_desc.replace('[default]', str(default)) + return pad(option_name, min_space) + subbed_desc + +#------------------------------------------------------------------------------- +# The high level interface between this script and the command line options +# supplied. Uses Option class and CmdLineParser to extract information from +# the command line and make it easily accessible. +#------------------------------------------------------------------------------- +var options = [] +var _opts = [] +var _banner = '' + +func add(name, default, desc): + options.append(Option.new(name, default, desc)) + +func get_value(name): + var found = false + var idx = 0 + + while(idx < options.size() and !found): + if(options[idx].option_name == name): + found = true + else: + idx += 1 + + if(found): + return options[idx].value + else: + print("COULD NOT FIND OPTION " + name) + return null + +func set_banner(banner): + _banner = banner + +func print_help(): + var longest = 0 + for i in range(options.size()): + if(options[i].option_name.length() > longest): + longest = options[i].option_name.length() + + print('---------------------------------------------------------') + print(_banner) + + print("\nOptions\n-------") + for i in range(options.size()): + print(' ' + options[i].to_s(longest + 2)) + print('---------------------------------------------------------') + +func print_options(): + for i in range(options.size()): + print(options[i].option_name + '=' + str(options[i].value)) + +func parse(): + var parser = CmdLineParser.new() + + for i in range(options.size()): + var t = typeof(options[i].default) + # only set values that were specified at the command line so that + # we can punch through default and config values correctly later. + # Without this check, you can't tell the difference between the + # defaults and what was specified, so you can't punch through + # higher level options. + if(parser.was_specified(options[i].option_name)): + if(t == TYPE_INT): + options[i].value = int(parser.get_value(options[i].option_name)) + elif(t == TYPE_STRING): + options[i].value = parser.get_value(options[i].option_name) + elif(t == TYPE_ARRAY): + options[i].value = parser.get_array_value(options[i].option_name) + elif(t == TYPE_BOOL): + options[i].value = parser.was_specified(options[i].option_name) + elif(t == TYPE_NIL): + print(options[i].option_name + ' cannot be processed, it has a nil datatype') + else: + print(options[i].option_name + ' cannot be processed, it has unknown datatype:' + str(t)) + + var unused = parser.get_unused_options() + if(unused.size() > 0): + print("Unrecognized options: ", unused) + return false + + return true diff --git a/test_proj/addons/gut/orphan_counter.gd b/test_proj/addons/gut/orphan_counter.gd new file mode 100644 index 0000000..cbfbea4 --- /dev/null +++ b/test_proj/addons/gut/orphan_counter.gd @@ -0,0 +1,55 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# This is a utility for tracking changes in the orphan count. Each time +# add_counter is called it adds/resets the value in the dictionary to the +# current number of orphans. Each call to get_counter will return the change +# in orphans since add_counter was last called. +# ############################################################################## +var _counters = {} + +func orphan_count(): + return Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT) + +func add_counter(name): + _counters[name] = orphan_count() + +# Returns the number of orphans created since add_counter was last called for +# the name. Returns -1 to avoid blowing up with an invalid name but still +# be somewhat visible that we've done something wrong. +func get_counter(name): + return orphan_count() - _counters[name] if _counters.has(name) else -1 + +func print_orphans(name, lgr): + var count = get_counter(name) + + if(count > 0): + var o = 'orphan' + if(count > 1): + o = 'orphans' + lgr.orphan(str(count, ' new ', o, '(', name, ').')) diff --git a/test_proj/addons/gut/parameter_factory.gd b/test_proj/addons/gut/parameter_factory.gd new file mode 100644 index 0000000..fe99e89 --- /dev/null +++ b/test_proj/addons/gut/parameter_factory.gd @@ -0,0 +1,75 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# This is the home for all parameter creation helpers. These functions should +# all return an array of values to be used as parameters for parameterized +# tests. +# ############################################################################## + +# ------------------------------------------------------------------------------ +# Creates an array of dictionaries. It pairs up the names array with each set +# of values in values. If more names than values are specified then the missing +# values will be filled with nulls. If more values than names are specified +# those values will be ignored. +# +# Example: +# create_named_parameters(['a', 'b'], [[1, 2], ['one', 'two']]) returns +# [{a:1, b:2}, {a:'one', b:'two'}] +# +# This allows you to increase readability of your parameterized tests: +# var params = create_named_parameters(['a', 'b'], [[1, 2], ['one', 'two']]) +# func test_foo(p = use_parameters(params)): +# assert_eq(p.a, p.b) +# +# Parameters: +# names: an array of names to be used as keys in the dictionaries +# values: an array of arrays of values. +# ------------------------------------------------------------------------------ +static func named_parameters(names, values): + var named = [] + for i in range(values.size()): + var entry = {} + + var parray = values[i] + if(typeof(parray) != TYPE_ARRAY): + parray = [values[i]] + + for j in range(names.size()): + if(j >= parray.size()): + entry[names[j]] = null + else: + entry[names[j]] = parray[j] + named.append(entry) + + return named + +# Additional Helper Ideas +# * File. IDK what it would look like. csv maybe. +# * Random values within a range? +# * All int values in a range or add an optioanal step. +# * \ No newline at end of file diff --git a/test_proj/addons/gut/parameter_handler.gd b/test_proj/addons/gut/parameter_handler.gd new file mode 100644 index 0000000..507ef71 --- /dev/null +++ b/test_proj/addons/gut/parameter_handler.gd @@ -0,0 +1,37 @@ +var _utils = load('res://addons/gut/utils.gd').get_instance() +var _params = null +var _call_count = 0 +var _logger = null + +func _init(params=null): + _params = params + _logger = _utils.get_logger() + if(typeof(_params) != TYPE_ARRAY): + _logger.error('You must pass an array to parameter_handler constructor.') + _params = null + + +func next_parameters(): + _call_count += 1 + return _params[_call_count -1] + +func get_current_parameters(): + return _params[_call_count] + +func is_done(): + var done = true + if(_params != null): + done = _call_count == _params.size() + return done + +func get_logger(): + return _logger + +func set_logger(logger): + _logger = logger + +func get_call_count(): + return _call_count + +func get_parameter_count(): + return _params.size() \ No newline at end of file diff --git a/test_proj/addons/gut/plugin.cfg b/test_proj/addons/gut/plugin.cfg new file mode 100644 index 0000000..b96a1fa --- /dev/null +++ b/test_proj/addons/gut/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Gut" +description="Unit Testing tool for Godot." +author="Butch Wesley" +version="7.0.0" +script="gut_plugin.gd" diff --git a/test_proj/addons/gut/plugin_control.gd b/test_proj/addons/gut/plugin_control.gd new file mode 100644 index 0000000..959ce17 --- /dev/null +++ b/test_proj/addons/gut/plugin_control.gd @@ -0,0 +1,247 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# This is the control that is added via the editor. It exposes GUT settings +# through the editor and delays the creation of the GUT instance until +# Engine.get_main_loop() works as expected. +# ############################################################################## +tool +extends Control + +# ------------------------------------------------------------------------------ +# GUT Settings +# ------------------------------------------------------------------------------ +export(String, 'AnonymousPro', 'CourierPrime', 'LobsterTwo', 'Default') var _font_name = 'AnonymousPro' +export(int) var _font_size = 20 +export(Color) var _font_color = Color(.8, .8, .8, 1) +export(Color) var _background_color = Color(.15, .15, .15, 1) +# Enable/Disable coloring of output. +export(bool) var _color_output = true +# The full/partial name of a script to select upon startup +export(String) var _select_script = '' +# The full/partial name of a test. All tests that contain the string will be +# run +export(String) var _tests_like = '' +# The full/partial name of an Inner Class to be run. All Inner Classes that +# contain the string will be run. +export(String) var _inner_class_name = '' +# Start running tests when the scene finishes loading +export var _run_on_load = false +# Maximize the GUT control on startup +export var _should_maximize = false +# Print output to the consol as well +export var _should_print_to_console = true +# Display orphan counts at the end of tests/scripts. +export var _show_orphans = true +# The log level. +export(int, 'Fail/Errors', 'Errors/Warnings/Test Names', 'Everything') var _log_level = 1 +# When enabled GUT will yield between tests to give the GUI time to paint. +# Disabling this can make the program appear to hang and can have some +# unwanted consequences with the timing of freeing objects +export var _yield_between_tests = true +# When GUT compares values it first checks the types to prevent runtime errors. +# This behavior can be disabled if desired. This flag was added early in +# development to prevent any breaking changes and will likely be removed in +# the future. +export var _disable_strict_datatype_checks = false +# The prefix used to find test methods. +export var _test_prefix = 'test_' +# The prefix used to find test scripts. +export var _file_prefix = 'test_' +# The file extension for test scripts (I don't think you can change this and +# everythign work). +export var _file_extension = '.gd' +# The prefix used to find Inner Test Classes. +export var _inner_class_prefix = 'Test' +# The directory GUT will use to write any temporary files. This isn't used +# much anymore since there was a change to the double creation implementation. +# This will be removed in a later release. +export(String) var _temp_directory = 'user://gut_temp_directory' +# The path and filename for exported test information. +export(String) var _export_path = '' +# When enabled, any directory added will also include its subdirectories when +# GUT looks for test scripts. +export var _include_subdirectories = false +# Allow user to add test directories via editor. This is done with strings +# instead of an array because the interface for editing arrays is really +# cumbersome and complicates testing because arrays set through the editor +# apply to ALL instances. This also allows the user to use the built in +# dialog to pick a directory. +export(String, DIR) var _directory1 = '' +export(String, DIR) var _directory2 = '' +export(String, DIR) var _directory3 = '' +export(String, DIR) var _directory4 = '' +export(String, DIR) var _directory5 = '' +export(String, DIR) var _directory6 = '' +# Must match the types in _utils for double strategy +export(int, 'FULL', 'PARTIAL') var _double_strategy = 1 +# Path and filename to the script to run before all tests are run. +export(String, FILE) var _pre_run_script = '' +# Path and filename to the script to run after all tests are run. +export(String, FILE) var _post_run_script = '' +# ------------------------------------------------------------------------------ + + +# ------------------------------------------------------------------------------ +# Signals +# ------------------------------------------------------------------------------ +# Emitted when all the tests have finished running. +signal tests_finished +# Emitted when GUT is ready to be interacted with, and before any tests are run. +signal gut_ready + + +# ------------------------------------------------------------------------------ +# Private stuff. +# ------------------------------------------------------------------------------ +var _gut = null +var _lgr = null +var _cancel_import = false +var _placeholder = null + +func _init(): + # This min size has to be what the min size of the GutScene's min size is + # but it has to be set here and not inferred i think. + rect_min_size = Vector2(740, 250) + +func _ready(): + # Must call this deferred so that there is enough time for + # Engine.get_main_loop() is populated and the psuedo singleton utils.gd + # can be setup correctly. + if(Engine.editor_hint): + _placeholder = load('res://addons/gut/GutScene.tscn').instance() + call_deferred('add_child', _placeholder) + _placeholder.rect_size = rect_size + else: + call_deferred('_setup_gut') + + connect('resized', self, '_on_resized') + +func _on_resized(): + if(_placeholder != null): + _placeholder.rect_size = rect_size + + +# Templates can be missing if tests are exported and the export config for the +# project does not include '*.txt' files. This check and related flags make +# sure GUT does not blow up and that the error is not lost in all the import +# output that is generated as well as ensuring that no tests are run. +# +# Assumption: This is only a concern when running from the scene since you +# cannot run GUT from the command line in an exported game. +func _check_for_templates(): + var f = File.new() + if(!f.file_exists('res://addons/gut/double_templates/function_template.txt')): + _lgr.error('Templates are missing. Make sure you are exporting "*.txt" or "addons/gut/double_templates/*.txt".') + _run_on_load = false + _cancel_import = true + return false + return true + +func _setup_gut(): + var _utils = load('res://addons/gut/utils.gd').get_instance() + + _lgr = _utils.get_logger() + _gut = load('res://addons/gut/gut.gd').new() + _gut.connect('tests_finished', self, '_on_tests_finished') + + if(!_check_for_templates()): + return + + _gut._select_script = _select_script + _gut._tests_like = _tests_like + _gut._inner_class_name = _inner_class_name + + _gut._test_prefix = _test_prefix + _gut._file_prefix = _file_prefix + _gut._file_extension = _file_extension + _gut._inner_class_prefix = _inner_class_prefix + _gut._temp_directory = _temp_directory + + _gut.set_should_maximize(_should_maximize) + _gut.set_yield_between_tests(_yield_between_tests) + _gut.disable_strict_datatype_checks(_disable_strict_datatype_checks) + _gut.set_export_path(_export_path) + _gut.set_include_subdirectories(_include_subdirectories) + _gut.set_double_strategy(_double_strategy) + _gut.set_pre_run_script(_pre_run_script) + _gut.set_post_run_script(_post_run_script) + _gut.set_color_output(_color_output) + _gut.show_orphans(_show_orphans) + + get_parent().add_child(_gut) + + if(!_utils.is_version_ok()): + return + + _gut.set_log_level(_log_level) + + _gut.add_directory(_directory1) + _gut.add_directory(_directory2) + _gut.add_directory(_directory3) + _gut.add_directory(_directory4) + _gut.add_directory(_directory5) + _gut.add_directory(_directory6) + + _gut.get_logger().disable_printer('console', !_should_print_to_console) + # When file logging enabled then the log will contain terminal escape + # strings. So when running the scene this is disabled. Also if enabled + # this may cause duplicate entries into the logs. + _gut.get_logger().disable_printer('terminal', true) + + _gut.get_gui().set_font_size(_font_size) + _gut.get_gui().set_font(_font_name) + _gut.get_gui().set_default_font_color(_font_color) + _gut.get_gui().set_background_color(_background_color) + _gut.get_gui().rect_size = rect_size + emit_signal('gut_ready') + + if(_run_on_load): + # Run the test scripts. If one has been selected then only run that one + # otherwise all tests will be run. + var run_rest_of_scripts = _select_script == null + _gut.test_scripts(run_rest_of_scripts) + +func _is_ready_to_go(action): + if(_gut == null): + push_error(str('GUT is not ready for ', action, ' yet. Perform actions on GUT in/after the gut_ready signal.')) + return _gut != null + +func _on_tests_finished(): + emit_signal('tests_finished') + +func get_gut(): + return _gut + +func export_if_tests_found(): + if(_is_ready_to_go('export_if_tests_found')): + _gut.export_if_tests_found() + +func import_tests_if_none_found(): + if(_is_ready_to_go('import_tests_if_none_found') and !_cancel_import): + _gut.import_tests_if_none_found() diff --git a/test_proj/addons/gut/printers.gd b/test_proj/addons/gut/printers.gd new file mode 100644 index 0000000..c543603 --- /dev/null +++ b/test_proj/addons/gut/printers.gd @@ -0,0 +1,157 @@ +# ------------------------------------------------------------------------------ +# Interface and some basic functionality for all printers. +# ------------------------------------------------------------------------------ +class Printer: + var _format_enabled = true + var _disabled = false + var _printer_name = 'NOT SET' + var _show_name = false # used for debugging, set manually + + func get_format_enabled(): + return _format_enabled + + func set_format_enabled(format_enabled): + _format_enabled = format_enabled + + func send(text, fmt=null): + if(_disabled): + return + + var formatted = text + if(fmt != null and _format_enabled): + formatted = format_text(text, fmt) + + if(_show_name): + formatted = str('(', _printer_name, ')') + formatted + + _output(formatted) + + func get_disabled(): + return _disabled + + func set_disabled(disabled): + _disabled = disabled + + # -------------------- + # Virtual Methods (some have some default behavior) + # -------------------- + func _output(text): + pass + + func format_text(text, fmt): + return text + +# ------------------------------------------------------------------------------ +# Responsible for sending text to a GUT gui. +# ------------------------------------------------------------------------------ +class GutGuiPrinter: + extends Printer + var _gut = null + + var _colors = { + red = Color.red, + yellow = Color.yellow, + green = Color.green + } + + func _init(): + _printer_name = 'gui' + + func _wrap_with_tag(text, tag): + return str('[', tag, ']', text, '[/', tag, ']') + + func _color_text(text, c_word): + return '[color=' + c_word + ']' + text + '[/color]' + + func format_text(text, fmt): + var box = _gut.get_gui().get_text_box() + + if(fmt == 'bold'): + box.push_bold() + elif(fmt == 'underline'): + box.push_underline() + elif(_colors.has(fmt)): + box.push_color(_colors[fmt]) + else: + # just pushing something to pop. + box.push_normal() + + box.add_text(text) + box.pop() + + return '' + + func _output(text): + _gut.get_gui().get_text_box().add_text(text) + + func get_gut(): + return _gut + + func set_gut(gut): + _gut = gut + + # This can be very very slow when the box has a lot of text. + func clear_line(): + var box = _gut.get_gui().get_text_box() + box.remove_line(box.get_line_count() - 1) + box.update() + +# ------------------------------------------------------------------------------ +# This AND TerminalPrinter should not be enabled at the same time since it will +# result in duplicate output. printraw does not print to the console so i had +# to make another one. +# ------------------------------------------------------------------------------ +class ConsolePrinter: + extends Printer + var _buffer = '' + + func _init(): + _printer_name = 'console' + + # suppresses output until it encounters a newline to keep things + # inline as much as possible. + func _output(text): + if(text.ends_with("\n")): + print(_buffer + text.left(text.length() -1)) + _buffer = '' + else: + _buffer += text + +# ------------------------------------------------------------------------------ +# Prints text to terminal, formats some words. +# ------------------------------------------------------------------------------ +class TerminalPrinter: + extends Printer + + var escape = PoolByteArray([0x1b]).get_string_from_ascii() + var cmd_colors = { + red = escape + '[31m', + yellow = escape + '[33m', + green = escape + '[32m', + + underline = escape + '[4m', + bold = escape + '[1m', + + default = escape + '[0m', + + clear_line = escape + '[2K' + } + + func _init(): + _printer_name = 'terminal' + + func _output(text): + # Note, printraw does not print to the console. + printraw(text) + + func format_text(text, fmt): + return cmd_colors[fmt] + text + cmd_colors.default + + func clear_line(): + send(cmd_colors.clear_line) + + func back(n): + send(escape + str('[', n, 'D')) + + func forward(n): + send(escape + str('[', n, 'C')) diff --git a/test_proj/addons/gut/signal_watcher.gd b/test_proj/addons/gut/signal_watcher.gd new file mode 100644 index 0000000..e127421 --- /dev/null +++ b/test_proj/addons/gut/signal_watcher.gd @@ -0,0 +1,166 @@ +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## + +# Some arbitrary string that should never show up by accident. If it does, then +# shame on you. +const ARG_NOT_SET = '_*_argument_*_is_*_not_set_*_' + +# This hash holds the objects that are being watched, the signals that are being +# watched, and an array of arrays that contains arguments that were passed +# each time the signal was emitted. +# +# For example: +# _watched_signals => { +# ref1 => { +# 'signal1' => [[], [], []], +# 'signal2' => [[p1, p2]], +# 'signal3' => [[p1]] +# }, +# ref2 => { +# 'some_signal' => [], +# 'other_signal' => [[p1, p2, p3], [p1, p2, p3], [p1, p2, p3]] +# } +# } +# +# In this sample: +# - signal1 on the ref1 object was emitted 3 times and each time, zero +# parameters were passed. +# - signal3 on ref1 was emitted once and passed a single parameter +# - some_signal on ref2 was never emitted. +# - other_signal on ref2 was emitted 3 times, each time with 3 parameters. +var _watched_signals = {} +var _utils = load('res://addons/gut/utils.gd').get_instance() + +func _add_watched_signal(obj, name): + # SHORTCIRCUIT - ignore dupes + if(_watched_signals.has(obj) and _watched_signals[obj].has(name)): + return + + if(!_watched_signals.has(obj)): + _watched_signals[obj] = {name:[]} + else: + _watched_signals[obj][name] = [] + obj.connect(name, self, '_on_watched_signal', [obj, name]) + +# This handles all the signals that are watched. It supports up to 9 parameters +# which could be emitted by the signal and the two parameters used when it is +# connected via watch_signal. I chose 9 since you can only specify up to 9 +# parameters when dynamically calling a method via call (per the Godot +# documentation, i.e. some_object.call('some_method', 1, 2, 3...)). +# +# Based on the documentation of emit_signal, it appears you can only pass up +# to 4 parameters when firing a signal. I haven't verified this, but this should +# future proof this some if the value ever grows. +func _on_watched_signal(arg1=ARG_NOT_SET, arg2=ARG_NOT_SET, arg3=ARG_NOT_SET, \ + arg4=ARG_NOT_SET, arg5=ARG_NOT_SET, arg6=ARG_NOT_SET, \ + arg7=ARG_NOT_SET, arg8=ARG_NOT_SET, arg9=ARG_NOT_SET, \ + arg10=ARG_NOT_SET, arg11=ARG_NOT_SET): + var args = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11] + + # strip off any unused vars. + var idx = args.size() -1 + while(str(args[idx]) == ARG_NOT_SET): + args.remove(idx) + idx -= 1 + + # retrieve object and signal name from the array and remove them. These + # will always be at the end since they are added when the connect happens. + var signal_name = args[args.size() -1] + args.pop_back() + var object = args[args.size() -1] + args.pop_back() + + _watched_signals[object][signal_name].append(args) + +func does_object_have_signal(object, signal_name): + var signals = object.get_signal_list() + for i in range(signals.size()): + if(signals[i]['name'] == signal_name): + return true + return false + +func watch_signals(object): + var signals = object.get_signal_list() + for i in range(signals.size()): + _add_watched_signal(object, signals[i]['name']) + +func watch_signal(object, signal_name): + var did = false + if(does_object_have_signal(object, signal_name)): + _add_watched_signal(object, signal_name) + did = true + return did + +func get_emit_count(object, signal_name): + var to_return = -1 + if(is_watching(object, signal_name)): + to_return = _watched_signals[object][signal_name].size() + return to_return + +func did_emit(object, signal_name): + var did = false + if(is_watching(object, signal_name)): + did = get_emit_count(object, signal_name) != 0 + return did + +func print_object_signals(object): + var list = object.get_signal_list() + for i in range(list.size()): + print(list[i].name, "\n ", list[i]) + +func get_signal_parameters(object, signal_name, index=-1): + var params = null + if(is_watching(object, signal_name)): + var all_params = _watched_signals[object][signal_name] + if(all_params.size() > 0): + if(index == -1): + index = all_params.size() -1 + params = all_params[index] + return params + +func is_watching_object(object): + return _watched_signals.has(object) + +func is_watching(object, signal_name): + return _watched_signals.has(object) and _watched_signals[object].has(signal_name) + +func clear(): + for obj in _watched_signals: + if(_utils.is_not_freed(obj)): + for signal_name in _watched_signals[obj]: + obj.disconnect(signal_name, self, '_on_watched_signal') + _watched_signals.clear() + +# Returns a list of all the signal names that were emitted by the object. +# If the object is not being watched then an empty list is returned. +func get_signals_emitted(obj): + var emitted = [] + if(is_watching_object(obj)): + for signal_name in _watched_signals[obj]: + if(_watched_signals[obj][signal_name].size() > 0): + emitted.append(signal_name) + + return emitted diff --git a/test_proj/addons/gut/source_code_pro.fnt b/test_proj/addons/gut/source_code_pro.fnt new file mode 100644 index 0000000000000000000000000000000000000000..3367650f77041f8020c39f0b1bb90d21e2ddf425 GIT binary patch literal 26499 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

F`ud^Dyusp94YMD$s2-p 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 at the +# end. Used for displaying the number of scripts without including all the +# Inner Classes. +func get_non_inner_class_script_count(): + var unique_scripts = {} + for i in range(_scripts.size()): + var ext_loc = _scripts[i].name.find_last('.gd.') + if(ext_loc == -1): + unique_scripts[_scripts[i].name] = 1 + else: + unique_scripts[_scripts[i].name.substr(0, ext_loc + 3)] = 1 + return unique_scripts.keys().size() + +func get_totals(): + var totals = { + passing = 0, + pending = 0, + failing = 0, + tests = 0, + scripts = 0 + } + + for s in range(_scripts.size()): + totals.passing += _scripts[s].get_pass_count() + totals.pending += _scripts[s].get_pending_count() + totals.failing += _scripts[s].get_fail_count() + totals.tests += _scripts[s]._test_order.size() + + totals.scripts = get_non_inner_class_script_count() + + return totals + +func log_summary_text(lgr): + var orig_indent = lgr.get_indent_level() + var found_failing_or_pending = false + + for s in range(_scripts.size()): + lgr.set_indent_level(0) + if(_scripts[s].get_fail_count() > 0 or _scripts[s].get_pending_count() > 0): + lgr.log(_scripts[s].name, lgr.fmts.underline) + + + for t in range(_scripts[s]._test_order.size()): + var tname = _scripts[s]._test_order[t] + var test = _scripts[s].get_test_obj(tname) + if(test.fail_texts.size() > 0 or test.pending_texts.size() > 0): + found_failing_or_pending = true + lgr.log(str('- ', tname)) + lgr.inc_indent() + + for i in range(test.fail_texts.size()): + lgr.failed(test.fail_texts[i]) + for i in range(test.pending_texts.size()): + lgr.pending(test.pending_texts[i]) + lgr.dec_indent() + + lgr.set_indent_level(0) + if(!found_failing_or_pending): + lgr.log('All tests passed', lgr.fmts.green) + + lgr.log() + var _totals = get_totals() + lgr.log("Totals", lgr.fmts.yellow) + lgr.log(str('Scripts: ', get_non_inner_class_script_count())) + lgr.log(str('Tests: ', _totals.tests)) + lgr.log(str('Passing asserts: ', _totals.passing)) + lgr.log(str('Failing asserts: ',_totals.failing)) + lgr.log(str('Pending: ', _totals.pending)) + + lgr.set_indent_level(orig_indent) + diff --git a/test_proj/addons/gut/test.gd b/test_proj/addons/gut/test.gd new file mode 100644 index 0000000..579cbfc --- /dev/null +++ b/test_proj/addons/gut/test.gd @@ -0,0 +1,1219 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# View readme for usage details. +# +# Version - see gut.gd +# ############################################################################## +# Class that all test scripts must extend. +# +# This provides all the asserts and other testing features. Test scripts are +# run by the Gut class in gut.gd +# ############################################################################## +extends Node + +# ------------------------------------------------------------------------------ +# Helper class to hold info for objects to double. This extracts info and has +# some convenience methods. This is key in being able to make the "smart double" +# method which makes doubling much easier for the user. +# ------------------------------------------------------------------------------ +class DoubleInfo: + var path + var subpath + var strategy + var make_partial + var extension + var _utils = load('res://addons/gut/utils.gd').get_instance() + var _is_native = false + + # Flexible init method. p2 can be subpath or stategy unless p3 is + # specified, then p2 must be subpath and p3 is strategy. + # + # Examples: + # (object_to_double) + # (object_to_double, subpath) + # (object_to_double, strategy) + # (object_to_double, subpath, strategy) + func _init(thing, p2=null, p3=null): + strategy = p2 + + if(typeof(p2) == TYPE_STRING): + strategy = p3 + subpath = p2 + + if(typeof(thing) == TYPE_OBJECT): + if(_utils.is_native_class(thing)): + path = thing + _is_native = true + extension = 'native_class_not_used' + else: + path = thing.resource_path + else: + path = thing + + if(!_is_native): + extension = path.get_extension() + + func is_scene(): + return extension == 'tscn' + + func is_script(): + return extension == 'gd' + + func is_native(): + return _is_native + +# ------------------------------------------------------------------------------ +# Begin test.gd +# ------------------------------------------------------------------------------ + +# constant for signal when calling yield_for +const YIELD = 'timeout' + +# Need a reference to the instance that is running the tests. This +# is set by the gut class when it runs the tests. This gets you +# access to the asserts in the tests you write. +var gut = null + +var passed = false # idk if this does anything TODO remove or document +var failed = false # idk if this does anything TODO remove or document +var _disable_strict_datatype_checks = false +# Holds all the text for a test's fail/pass. This is used for testing purposes +# to see the text of a failed sub-test in test_test.gd +var _fail_pass_text = [] + + +const EDITOR_PROPERTY = PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_DEFAULT +const VARIABLE_PROPERTY = PROPERTY_USAGE_SCRIPT_VARIABLE + +# Summary counts for the test. +var _summary = { + asserts = 0, + passed = 0, + failed = 0, + tests = 0, + pending = 0 +} + +# This is used to watch signals so we can make assertions about them. +var _signal_watcher = load('res://addons/gut/signal_watcher.gd').new() + +# Convenience copy of _utils.DOUBLE_STRATEGY +var DOUBLE_STRATEGY = null +var _utils = load('res://addons/gut/utils.gd').get_instance() +var _lgr = _utils.get_logger() +var _strutils = _utils.Strutils.new() +# syntax sugar +var ParameterFactory = _utils.ParameterFactory + +func _init(): + DOUBLE_STRATEGY = _utils.DOUBLE_STRATEGY # yes, this is right + +func _str(thing): + return _strutils.type2str(thing) + +# ------------------------------------------------------------------------------ +# Fail an assertion. Causes test and script to fail as well. +# ------------------------------------------------------------------------------ +func _fail(text): + _summary.asserts += 1 + _summary.failed += 1 + _fail_pass_text.append('failed: ' + text) + if(gut): + _lgr.failed(text) + gut._fail(text) + +# ------------------------------------------------------------------------------ +# Pass an assertion. +# ------------------------------------------------------------------------------ +func _pass(text): + _summary.asserts += 1 + _summary.passed += 1 + _fail_pass_text.append('passed: ' + text) + if(gut): + _lgr.passed(text) + gut._pass(text) + +# ------------------------------------------------------------------------------ +# Checks if the datatypes passed in match. If they do not then this will cause +# a fail to occur. If they match then TRUE is returned, FALSE if not. This is +# used in all the assertions that compare values. +# ------------------------------------------------------------------------------ +func _do_datatypes_match__fail_if_not(got, expected, text): + var did_pass = true + + if(!_disable_strict_datatype_checks): + var got_type = typeof(got) + var expect_type = typeof(expected) + if(got_type != expect_type and got != null and expected != null): + # If we have a mismatch between float and int (types 2 and 3) then + # print out a warning but do not fail. + if([2, 3].has(got_type) and [2, 3].has(expect_type)): + _lgr.warn(str('Warn: Float/Int comparison. Got ', _strutils.types[got_type], + ' but expected ', _strutils.types[expect_type])) + else: + _fail('Cannot compare ' + _strutils.types[got_type] + '[' + _str(got) + '] to ' + \ + _strutils.types[expect_type] + '[' + _str(expected) + ']. ' + text) + did_pass = false + + return did_pass + +# ------------------------------------------------------------------------------ +# Create a string that lists all the methods that were called on an spied +# instance. +# ------------------------------------------------------------------------------ +func _get_desc_of_calls_to_instance(inst): + var BULLET = ' * ' + var calls = gut.get_spy().get_call_list_as_string(inst) + # indent all the calls + calls = BULLET + calls.replace("\n", "\n" + BULLET) + # remove trailing newline and bullet + calls = calls.substr(0, calls.length() - BULLET.length() - 1) + return "Calls made on " + str(inst) + "\n" + calls + +# ------------------------------------------------------------------------------ +# Signal assertion helper. Do not call directly, use _can_make_signal_assertions +# ------------------------------------------------------------------------------ +func _fail_if_does_not_have_signal(object, signal_name): + var did_fail = false + if(!_signal_watcher.does_object_have_signal(object, signal_name)): + _fail(str('Object ', object, ' does not have the signal [', signal_name, ']')) + did_fail = true + return did_fail + +# ------------------------------------------------------------------------------ +# Signal assertion helper. Do not call directly, use _can_make_signal_assertions +# ------------------------------------------------------------------------------ +func _fail_if_not_watching(object): + var did_fail = false + if(!_signal_watcher.is_watching_object(object)): + _fail(str('Cannot make signal assertions because the object ', object, \ + ' is not being watched. Call watch_signals(some_object) to be able to make assertions about signals.')) + did_fail = true + return did_fail + +# ------------------------------------------------------------------------------ +# Returns text that contains original text and a list of all the signals that +# were emitted for the passed in object. +# ------------------------------------------------------------------------------ +func _get_fail_msg_including_emitted_signals(text, object): + return str(text," (Signals emitted: ", _signal_watcher.get_signals_emitted(object), ")") + +# ------------------------------------------------------------------------------ +# This validates that parameters is an array and generates a specific error +# and a failure with a specific message +# ------------------------------------------------------------------------------ +func _fail_if_parameters_not_array(parameters): + var invalid = parameters != null and typeof(parameters) != TYPE_ARRAY + if(invalid): + _lgr.error('The "parameters" parameter must be an array of expected parameter values.') + _fail('Cannot compare paramter values because an array was not passed.') + return invalid +# ####################### +# Virtual Methods +# ####################### + +# alias for prerun_setup +func before_all(): + pass + +# alias for setup +func before_each(): + pass + +# alias for postrun_teardown +func after_all(): + pass + +# alias for teardown +func after_each(): + pass + +# ####################### +# Public +# ####################### + +func get_logger(): + return _lgr + +func set_logger(logger): + _lgr = logger + + +# ####################### +# Asserts +# ####################### + +# ------------------------------------------------------------------------------ +# Asserts that the expected value equals the value got. +# ------------------------------------------------------------------------------ +func assert_eq(got, expected, text=""): + var disp = "[" + _str(got) + "] expected to equal [" + _str(expected) + "]: " + text + if(_do_datatypes_match__fail_if_not(got, expected, text)): + if(expected != got): + _fail(disp) + else: + _pass(disp) + +# ------------------------------------------------------------------------------ +# Asserts that the value got does not equal the "not expected" value. +# ------------------------------------------------------------------------------ +func assert_ne(got, not_expected, text=""): + var disp = "[" + _str(got) + "] expected to be anything except [" + _str(not_expected) + "]: " + text + if(_do_datatypes_match__fail_if_not(got, not_expected, text)): + if(got == not_expected): + _fail(disp) + else: + _pass(disp) + +# ------------------------------------------------------------------------------ +# Asserts that the expected value almost equals the value got. +# ------------------------------------------------------------------------------ +func assert_almost_eq(got, expected, error_interval, text=''): + var disp = "[" + _str(got) + "] expected to equal [" + _str(expected) + "] +/- [" + str(error_interval) + "]: " + text + if(_do_datatypes_match__fail_if_not(got, expected, text) and _do_datatypes_match__fail_if_not(got, error_interval, text)): + if(got < (expected - error_interval) or got > (expected + error_interval)): + _fail(disp) + else: + _pass(disp) + +# ------------------------------------------------------------------------------ +# Asserts that the expected value does not almost equal the value got. +# ------------------------------------------------------------------------------ +func assert_almost_ne(got, not_expected, error_interval, text=''): + var disp = "[" + _str(got) + "] expected to not equal [" + _str(not_expected) + "] +/- [" + str(error_interval) + "]: " + text + if(_do_datatypes_match__fail_if_not(got, not_expected, text) and _do_datatypes_match__fail_if_not(got, error_interval, text)): + if(got < (not_expected - error_interval) or got > (not_expected + error_interval)): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Asserts got is greater than expected +# ------------------------------------------------------------------------------ +func assert_gt(got, expected, text=""): + var disp = "[" + _str(got) + "] expected to be > than [" + _str(expected) + "]: " + text + if(_do_datatypes_match__fail_if_not(got, expected, text)): + if(got > expected): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Asserts got is less than expected +# ------------------------------------------------------------------------------ +func assert_lt(got, expected, text=""): + var disp = "[" + _str(got) + "] expected to be < than [" + _str(expected) + "]: " + text + if(_do_datatypes_match__fail_if_not(got, expected, text)): + if(got < expected): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# asserts that got is true +# ------------------------------------------------------------------------------ +func assert_true(got, text=""): + if(!got): + _fail(text) + else: + _pass(text) + +# ------------------------------------------------------------------------------ +# Asserts that got is false +# ------------------------------------------------------------------------------ +func assert_false(got, text=""): + if(got): + _fail(text) + else: + _pass(text) + +# ------------------------------------------------------------------------------ +# Asserts value is between (inclusive) the two expected values. +# ------------------------------------------------------------------------------ +func assert_between(got, expect_low, expect_high, text=""): + var disp = "[" + _str(got) + "] expected to be between [" + _str(expect_low) + "] and [" + str(expect_high) + "]: " + text + + if(_do_datatypes_match__fail_if_not(got, expect_low, text) and _do_datatypes_match__fail_if_not(got, expect_high, text)): + if(expect_low > expect_high): + disp = "INVALID range. [" + str(expect_low) + "] is not less than [" + str(expect_high) + "]" + _fail(disp) + else: + if(got < expect_low or got > expect_high): + _fail(disp) + else: + _pass(disp) + +# ------------------------------------------------------------------------------ +# Uses the 'has' method of the object passed in to determine if it contains +# the passed in element. +# ------------------------------------------------------------------------------ +func assert_has(obj, element, text=""): + var disp = str('Expected [', _str(obj), '] to contain value: [', _str(element), ']: ', text) + if(obj.has(element)): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func assert_does_not_have(obj, element, text=""): + var disp = str('Expected [', _str(obj), '] to NOT contain value: [', _str(element), ']: ', text) + if(obj.has(element)): + _fail(disp) + else: + _pass(disp) + +# ------------------------------------------------------------------------------ +# Asserts that a file exists +# ------------------------------------------------------------------------------ +func assert_file_exists(file_path): + var disp = 'expected [' + file_path + '] to exist.' + var f = File.new() + if(f.file_exists(file_path)): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Asserts that a file should not exist +# ------------------------------------------------------------------------------ +func assert_file_does_not_exist(file_path): + var disp = 'expected [' + file_path + '] to NOT exist' + var f = File.new() + if(!f.file_exists(file_path)): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Asserts the specified file is empty +# ------------------------------------------------------------------------------ +func assert_file_empty(file_path): + var disp = 'expected [' + file_path + '] to be empty' + var f = File.new() + if(f.file_exists(file_path) and gut.is_file_empty(file_path)): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Asserts the specified file is not empty +# ------------------------------------------------------------------------------ +func assert_file_not_empty(file_path): + var disp = 'expected [' + file_path + '] to contain data' + if(!gut.is_file_empty(file_path)): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Asserts the object has the specified method +# ------------------------------------------------------------------------------ +func assert_has_method(obj, method): + assert_true(obj.has_method(method), _str(obj) + ' should have method: ' + method) + +# Old deprecated method name +func assert_get_set_methods(obj, property, default, set_to): + _lgr.deprecated('assert_get_set_methods', 'assert_accessors') + assert_accessors(obj, property, default, set_to) + +# ------------------------------------------------------------------------------ +# Verifies the object has get and set methods for the property passed in. The +# property isn't tied to anything, just a name to be appended to the end of +# get_ and set_. Asserts the get_ and set_ methods exist, if not, it stops there. +# If they exist then it asserts get_ returns the expected default then calls +# set_ and asserts get_ has the value it was set to. +# ------------------------------------------------------------------------------ +func assert_accessors(obj, property, default, set_to): + var fail_count = _summary.failed + var get = 'get_' + property + var set = 'set_' + property + assert_has_method(obj, get) + assert_has_method(obj, set) + # SHORT CIRCUIT + if(_summary.failed > fail_count): + return + assert_eq(obj.call(get), default, 'It should have the expected default value.') + obj.call(set, set_to) + assert_eq(obj.call(get), set_to, 'The set value should have been returned.') + + +# --------------------------------------------------------------------------- +# Property search helper. Used to retrieve Dictionary of specified property +# from passed object. Returns null if not found. +# If provided, property_usage constrains the type of property returned by +# passing either: +# EDITOR_PROPERTY for properties defined as: export(int) var some_value +# VARIABLE_PROPERTY for properties defunded as: var another_value +# --------------------------------------------------------------------------- +func _find_object_property(obj, property_name, property_usage=null): + var result = null + var found = false + var properties = obj.get_property_list() + + while !found and !properties.empty(): + var property = properties.pop_back() + if property['name'] == property_name: + if property_usage == null or property['usage'] == property_usage: + result = property + found = true + return result + +# ------------------------------------------------------------------------------ +# Asserts a class exports a variable. +# ------------------------------------------------------------------------------ +func assert_exports(obj, property_name, type): + var disp = 'expected %s to have editor property [%s]' % [_str(obj), property_name] + var property = _find_object_property(obj, property_name, EDITOR_PROPERTY) + if property != null: + disp += ' of type [%s]. Got type [%s].' % [_strutils.types[type], _strutils.types[property['type']]] + if property['type'] == type: + _pass(disp) + else: + _fail(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Signal assertion helper. +# +# Verifies that the object and signal are valid for making signal assertions. +# This will fail with specific messages that indicate why they are not valid. +# This returns true/false to indicate if the object and signal are valid. +# ------------------------------------------------------------------------------ +func _can_make_signal_assertions(object, signal_name): + return !(_fail_if_not_watching(object) or _fail_if_does_not_have_signal(object, signal_name)) + +# ------------------------------------------------------------------------------ +# Check if an object is connected to a signal on another object. Returns True +# if it is and false otherwise +# ------------------------------------------------------------------------------ +func _is_connected(signaler_obj, connect_to_obj, signal_name, method_name=""): + if(method_name != ""): + return signaler_obj.is_connected(signal_name, connect_to_obj, method_name) + else: + var connections = signaler_obj.get_signal_connection_list(signal_name) + for conn in connections: + if((conn.source == signaler_obj) and (conn.target == connect_to_obj)): + return true + return false +# ------------------------------------------------------------------------------ +# Watch the signals for an object. This must be called before you can make +# any assertions about the signals themselves. +# ------------------------------------------------------------------------------ +func watch_signals(object): + _signal_watcher.watch_signals(object) + +# ------------------------------------------------------------------------------ +# Asserts that an object is connected to a signal on another object +# +# This will fail with specific messages if the target object is not connected +# to the specified signal on the source object. +# ------------------------------------------------------------------------------ +func assert_connected(signaler_obj, connect_to_obj, signal_name, method_name=""): + pass + var method_disp = '' + if (method_name != ""): + method_disp = str(' using method: [', method_name, '] ') + var disp = str('Expected object ', _str(signaler_obj),\ + ' to be connected to signal: [', signal_name, '] on ',\ + _str(connect_to_obj), method_disp) + if(_is_connected(signaler_obj, connect_to_obj, signal_name, method_name)): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Asserts that an object is not connected to a signal on another object +# +# This will fail with specific messages if the target object is connected +# to the specified signal on the source object. +# ------------------------------------------------------------------------------ +func assert_not_connected(signaler_obj, connect_to_obj, signal_name, method_name=""): + var method_disp = '' + if (method_name != ""): + method_disp = str(' using method: [', method_name, '] ') + var disp = str('Expected object ', _str(signaler_obj),\ + ' to not be connected to signal: [', signal_name, '] on ',\ + _str(connect_to_obj), method_disp) + if(_is_connected(signaler_obj, connect_to_obj, signal_name, method_name)): + _fail(disp) + else: + _pass(disp) + +# ------------------------------------------------------------------------------ +# Asserts that a signal has been emitted at least once. +# +# This will fail with specific messages if the object is not being watched or +# the object does not have the specified signal +# ------------------------------------------------------------------------------ +func assert_signal_emitted(object, signal_name, text=""): + var disp = str('Expected object ', _str(object), ' to have emitted signal [', signal_name, ']: ', text) + if(_can_make_signal_assertions(object, signal_name)): + if(_signal_watcher.did_emit(object, signal_name)): + _pass(disp) + else: + _fail(_get_fail_msg_including_emitted_signals(disp, object)) + +# ------------------------------------------------------------------------------ +# Asserts that a signal has not been emitted. +# +# This will fail with specific messages if the object is not being watched or +# the object does not have the specified signal +# ------------------------------------------------------------------------------ +func assert_signal_not_emitted(object, signal_name, text=""): + var disp = str('Expected object ', _str(object), ' to NOT emit signal [', signal_name, ']: ', text) + if(_can_make_signal_assertions(object, signal_name)): + if(_signal_watcher.did_emit(object, signal_name)): + _fail(disp) + else: + _pass(disp) + +# ------------------------------------------------------------------------------ +# Asserts that a signal was fired with the specified parameters. The expected +# parameters should be passed in as an array. An optional index can be passed +# when a signal has fired more than once. The default is to retrieve the most +# recent emission of the signal. +# +# This will fail with specific messages if the object is not being watched or +# the object does not have the specified signal +# ------------------------------------------------------------------------------ +func assert_signal_emitted_with_parameters(object, signal_name, parameters, index=-1): + var disp = str('Expected object ', _str(object), ' to emit signal [', signal_name, '] with parameters ', parameters, ', got ') + if(_can_make_signal_assertions(object, signal_name)): + if(_signal_watcher.did_emit(object, signal_name)): + var parms_got = _signal_watcher.get_signal_parameters(object, signal_name, index) + if(parameters == parms_got): + _pass(str(disp, parms_got)) + else: + _fail(str(disp, parms_got)) + else: + var text = str('Object ', object, ' did not emit signal [', signal_name, ']') + _fail(_get_fail_msg_including_emitted_signals(text, object)) + +# ------------------------------------------------------------------------------ +# Assert that a signal has been emitted a specific number of times. +# +# This will fail with specific messages if the object is not being watched or +# the object does not have the specified signal +# ------------------------------------------------------------------------------ +func assert_signal_emit_count(object, signal_name, times, text=""): + + if(_can_make_signal_assertions(object, signal_name)): + var count = _signal_watcher.get_emit_count(object, signal_name) + var disp = str('Expected the signal [', signal_name, '] emit count of [', count, '] to equal [', times, ']: ', text) + if(count== times): + _pass(disp) + else: + _fail(_get_fail_msg_including_emitted_signals(disp, object)) + +# ------------------------------------------------------------------------------ +# Assert that the passed in object has the specified signal +# ------------------------------------------------------------------------------ +func assert_has_signal(object, signal_name, text=""): + var disp = str('Expected object ', _str(object), ' to have signal [', signal_name, ']: ', text) + if(_signal_watcher.does_object_have_signal(object, signal_name)): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Returns the number of times a signal was emitted. -1 returned if the object +# is not being watched. +# ------------------------------------------------------------------------------ +func get_signal_emit_count(object, signal_name): + return _signal_watcher.get_emit_count(object, signal_name) + +# ------------------------------------------------------------------------------ +# Get the parmaters of a fired signal. If the signal was not fired null is +# returned. You can specify an optional index (use get_signal_emit_count to +# determine the number of times it was emitted). The default index is the +# latest time the signal was fired (size() -1 insetead of 0). The parameters +# returned are in an array. +# ------------------------------------------------------------------------------ +func get_signal_parameters(object, signal_name, index=-1): + return _signal_watcher.get_signal_parameters(object, signal_name, index) + +# ------------------------------------------------------------------------------ +# Get the parameters for a method call to a doubled object. By default it will +# return the most recent call. You can optionally specify an index. +# +# Returns: +# * an array of parameter values if a call the method was found +# * null when a call to the method was not found or the index specified was +# invalid. +# ------------------------------------------------------------------------------ +func get_call_parameters(object, method_name, index=-1): + var to_return = null + if(_utils.is_double(object)): + to_return = gut.get_spy().get_call_parameters(object, method_name, index) + else: + _lgr.error('You must pass a doulbed object to get_call_parameters.') + + return to_return + +# ------------------------------------------------------------------------------ +# Assert that object is an instance of a_class +# ------------------------------------------------------------------------------ +func assert_extends(object, a_class, text=''): + _lgr.deprecated('assert_extends', 'assert_is') + assert_is(object, a_class, text) + +# Alias for assert_extends +func assert_is(object, a_class, text=''): + var disp = str('Expected [', _str(object), '] to be type of [', a_class, ']: ', text) + var NATIVE_CLASS = 'GDScriptNativeClass' + var GDSCRIPT_CLASS = 'GDScript' + var bad_param_2 = 'Parameter 2 must be a Class (like Node2D or Label). You passed ' + + if(typeof(object) != TYPE_OBJECT): + _fail(str('Parameter 1 must be an instance of an object. You passed: ', _str(object))) + elif(typeof(a_class) != TYPE_OBJECT): + _fail(str(bad_param_2, _str(a_class))) + else: + disp = str('Expected [', _str(object), '] to extend [', _str(a_class), ']: ', text) + if(a_class.get_class() != NATIVE_CLASS and a_class.get_class() != GDSCRIPT_CLASS): + _fail(str(bad_param_2, _str(a_class))) + else: + if(object is a_class): + _pass(disp) + else: + _fail(disp) + +func _get_typeof_string(the_type): + var to_return = "" + if(_strutils.types.has(the_type)): + to_return += str(the_type, '(', _strutils.types[the_type], ')') + else: + to_return += str(the_type) + return to_return + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func assert_typeof(object, type, text=''): + var disp = str('Expected [typeof(', object, ') = ') + disp += _get_typeof_string(typeof(object)) + disp += '] to equal [' + disp += _get_typeof_string(type) + ']' + disp += '. ' + text + if(typeof(object) == type): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func assert_not_typeof(object, type, text=''): + var disp = str('Expected [typeof(', object, ') = ') + disp += _get_typeof_string(typeof(object)) + disp += '] to not equal [' + disp += _get_typeof_string(type) + ']' + disp += '. ' + text + if(typeof(object) != type): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Assert that text contains given search string. +# The match_case flag determines case sensitivity. +# ------------------------------------------------------------------------------ +func assert_string_contains(text, search, match_case=true): + var empty_search = 'Expected text and search strings to be non-empty. You passed \'%s\' and \'%s\'.' + var disp = 'Expected \'%s\' to contain \'%s\', match_case=%s' % [text, search, match_case] + if(text == '' or search == ''): + _fail(empty_search % [text, search]) + elif(match_case): + if(text.find(search) == -1): + _fail(disp) + else: + _pass(disp) + else: + if(text.to_lower().find(search.to_lower()) == -1): + _fail(disp) + else: + _pass(disp) + +# ------------------------------------------------------------------------------ +# Assert that text starts with given search string. +# match_case flag determines case sensitivity. +# ------------------------------------------------------------------------------ +func assert_string_starts_with(text, search, match_case=true): + var empty_search = 'Expected text and search strings to be non-empty. You passed \'%s\' and \'%s\'.' + var disp = 'Expected \'%s\' to start with \'%s\', match_case=%s' % [text, search, match_case] + if(text == '' or search == ''): + _fail(empty_search % [text, search]) + elif(match_case): + if(text.find(search) == 0): + _pass(disp) + else: + _fail(disp) + else: + if(text.to_lower().find(search.to_lower()) == 0): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Assert that text ends with given search string. +# match_case flag determines case sensitivity. +# ------------------------------------------------------------------------------ +func assert_string_ends_with(text, search, match_case=true): + var empty_search = 'Expected text and search strings to be non-empty. You passed \'%s\' and \'%s\'.' + var disp = 'Expected \'%s\' to end with \'%s\', match_case=%s' % [text, search, match_case] + var required_index = len(text) - len(search) + if(text == '' or search == ''): + _fail(empty_search % [text, search]) + elif(match_case): + if(text.find(search) == required_index): + _pass(disp) + else: + _fail(disp) + else: + if(text.to_lower().find(search.to_lower()) == required_index): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Assert that a method was called on an instance of a doubled class. If +# parameters are supplied then the params passed in when called must match. +# TODO make 3rd parameter "param_or_text" and add fourth parameter of "text" and +# then work some magic so this can have a "text" parameter without being +# annoying. +# ------------------------------------------------------------------------------ +func assert_called(inst, method_name, parameters=null): + var disp = str('Expected [',method_name,'] to have been called on ',_str(inst)) + + if(_fail_if_parameters_not_array(parameters)): + return + + if(!_utils.is_double(inst)): + _fail('You must pass a doubled instance to assert_called. Check the wiki for info on using double.') + else: + if(gut.get_spy().was_called(inst, method_name, parameters)): + _pass(disp) + else: + if(parameters != null): + disp += str(' with parameters ', parameters) + _fail(str(disp, "\n", _get_desc_of_calls_to_instance(inst))) + +# ------------------------------------------------------------------------------ +# Assert that a method was not called on an instance of a doubled class. If +# parameters are specified then this will only fail if it finds a call that was +# sent matching parameters. +# ------------------------------------------------------------------------------ +func assert_not_called(inst, method_name, parameters=null): + var disp = str('Expected [', method_name, '] to NOT have been called on ', _str(inst)) + + if(_fail_if_parameters_not_array(parameters)): + return + + if(!_utils.is_double(inst)): + _fail('You must pass a doubled instance to assert_not_called. Check the wiki for info on using double.') + else: + if(gut.get_spy().was_called(inst, method_name, parameters)): + if(parameters != null): + disp += str(' with parameters ', parameters) + _fail(str(disp, "\n", _get_desc_of_calls_to_instance(inst))) + else: + _pass(disp) + +# ------------------------------------------------------------------------------ +# Assert that a method on an instance of a doubled class was called a number +# of times. If parameters are specified then only calls with matching +# parameter values will be counted. +# ------------------------------------------------------------------------------ +func assert_call_count(inst, method_name, expected_count, parameters=null): + var count = gut.get_spy().call_count(inst, method_name, parameters) + + if(_fail_if_parameters_not_array(parameters)): + return + + var param_text = '' + if(parameters): + param_text = ' with parameters ' + str(parameters) + var disp = 'Expected [%s] on %s to be called [%s] times%s. It was called [%s] times.' + disp = disp % [method_name, _str(inst), expected_count, param_text, count] + + if(!_utils.is_double(inst)): + _fail('You must pass a doubled instance to assert_call_count. Check the wiki for info on using double.') + else: + if(count == expected_count): + _pass(disp) + else: + _fail(str(disp, "\n", _get_desc_of_calls_to_instance(inst))) + +# ------------------------------------------------------------------------------ +# Asserts the passed in value is null +# ------------------------------------------------------------------------------ +func assert_null(got, text=''): + var disp = str('Expected [', _str(got), '] to be NULL: ', text) + if(got == null): + _pass(disp) + else: + _fail(disp) + +# ------------------------------------------------------------------------------ +# Asserts the passed in value is null +# ------------------------------------------------------------------------------ +func assert_not_null(got, text=''): + var disp = str('Expected [', _str(got), '] to be anything but NULL: ', text) + if(got == null): + _fail(disp) + else: + _pass(disp) + +# ----------------------------------------------------------------------------- +# Asserts object has been freed from memory +# We pass in a title (since if it is freed, we lost all identity data) +# ----------------------------------------------------------------------------- +func assert_freed(obj, title): + var disp = title + if(is_instance_valid(obj)): + disp = _strutils.type2str(obj) + title + assert_true(not is_instance_valid(obj), "Expected [%s] to be freed" % disp) + +# ------------------------------------------------------------------------------ +# Asserts Object has not been freed from memory +# ----------------------------------------------------------------------------- +func assert_not_freed(obj, title): + var disp = title + if(is_instance_valid(obj)): + disp = _strutils.type2str(obj) + title + assert_true(is_instance_valid(obj), "Expected [%s] to not be freed" % disp) + +# ------------------------------------------------------------------------------ +# Asserts that the current test has not introduced any new orphans. This only +# applies to the test code that preceedes a call to this method so it should be +# the last thing your test does. +# ------------------------------------------------------------------------------ +func assert_no_new_orphans(text=''): + var count = gut.get_orphan_counter().get_counter('test') + var msg = '' + if(text != ''): + msg = ': ' + text + # Note that get_counter will return -1 if the counter does not exist. This + # can happen with a misplaced assert_no_new_orphans. Checking for > 0 + # ensures this will not cause some weird failure. + if(count > 0): + _fail(str('Expected no orphans, but found ', count, msg)) + else: + _pass('No new orphans found.' + msg) + +# ------------------------------------------------------------------------------ +# Mark the current test as pending. +# ------------------------------------------------------------------------------ +func pending(text=""): + _summary.pending += 1 + if(gut): + _lgr.pending(text) + gut._pending(text) + +# ------------------------------------------------------------------------------ +# Returns the number of times a signal was emitted. -1 returned if the object +# is not being watched. +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +# Yield for the time sent in. The optional message will be printed when +# Gut detects the yield. When the time expires the YIELD signal will be +# emitted. +# ------------------------------------------------------------------------------ +func yield_for(time, msg=''): + return gut.set_yield_time(time, msg) + +# ------------------------------------------------------------------------------ +# Yield to a signal or a maximum amount of time, whichever comes first. When +# the conditions are met the YIELD signal will be emitted. +# ------------------------------------------------------------------------------ +func yield_to(obj, signal_name, max_wait, msg=''): + watch_signals(obj) + gut.set_yield_signal_or_time(obj, signal_name, max_wait, msg) + + return gut + +# ------------------------------------------------------------------------------ +# Ends a test that had a yield in it. You only need to use this if you do +# not make assertions after a yield. +# ------------------------------------------------------------------------------ +func end_test(): + _lgr.deprecated('end_test is no longer necessary, you can remove it.') + #gut.end_yielded_test() + +func get_summary(): + return _summary + +func get_fail_count(): + return _summary.failed + +func get_pass_count(): + return _summary.passed + +func get_pending_count(): + return _summary.pending + +func get_assert_count(): + return _summary.asserts + +func clear_signal_watcher(): + _signal_watcher.clear() + +func get_double_strategy(): + return gut.get_doubler().get_strategy() + +func set_double_strategy(double_strategy): + gut.get_doubler().set_strategy(double_strategy) + +func pause_before_teardown(): + gut.pause_before_teardown() +# ------------------------------------------------------------------------------ +# Convert the _summary dictionary into text +# ------------------------------------------------------------------------------ +func get_summary_text(): + var to_return = get_script().get_path() + "\n" + to_return += str(' ', _summary.passed, ' of ', _summary.asserts, ' passed.') + if(_summary.pending > 0): + to_return += str("\n ", _summary.pending, ' pending') + if(_summary.failed > 0): + to_return += str("\n ", _summary.failed, ' failed.') + return to_return + +# ------------------------------------------------------------------------------ +# Double a script, inner class, or scene using a path or a loaded script/scene. +# +# +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func _smart_double(double_info): + var override_strat = _utils.nvl(double_info.strategy, gut.get_doubler().get_strategy()) + var to_return = null + + if(double_info.is_scene()): + if(double_info.make_partial): + to_return = gut.get_doubler().partial_double_scene(double_info.path, override_strat) + else: + to_return = gut.get_doubler().double_scene(double_info.path, override_strat) + + elif(double_info.is_native()): + if(double_info.make_partial): + to_return = gut.get_doubler().partial_double_gdnative(double_info.path) + else: + to_return = gut.get_doubler().double_gdnative(double_info.path) + + elif(double_info.is_script()): + if(double_info.subpath == null): + if(double_info.make_partial): + to_return = gut.get_doubler().partial_double(double_info.path, override_strat) + else: + to_return = gut.get_doubler().double(double_info.path, override_strat) + else: + if(double_info.make_partial): + to_return = gut.get_doubler().partial_double_inner(double_info.path, double_info.subpath, override_strat) + else: + to_return = gut.get_doubler().double_inner(double_info.path, double_info.subpath, override_strat) + return to_return + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func double(thing, p2=null, p3=null): + var double_info = DoubleInfo.new(thing, p2, p3) + double_info.make_partial = false + + return _smart_double(double_info) + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +func partial_double(thing, p2=null, p3=null): + var double_info = DoubleInfo.new(thing, p2, p3) + double_info.make_partial = true + + return _smart_double(double_info) + + +# ------------------------------------------------------------------------------ +# Specifically double a scene +# ------------------------------------------------------------------------------ +func double_scene(path, strategy=null): + var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy()) + return gut.get_doubler().double_scene(path, override_strat) + +# ------------------------------------------------------------------------------ +# Specifically double a script +# ------------------------------------------------------------------------------ +func double_script(path, strategy=null): + var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy()) + return gut.get_doubler().double(path, override_strat) + +# ------------------------------------------------------------------------------ +# Specifically double an Inner class in a a script +# ------------------------------------------------------------------------------ +func double_inner(path, subpath, strategy=null): + var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy()) + return gut.get_doubler().double_inner(path, subpath, override_strat) + +# ------------------------------------------------------------------------------ +# Add a method that the doubler will ignore. You can pass this the path to a +# script or scene or a loaded script or scene. When running tests, these +# ignores are cleared after every test. +# ------------------------------------------------------------------------------ +func ignore_method_when_doubling(thing, method_name): + var double_info = DoubleInfo.new(thing) + var path = double_info.path + + if(double_info.is_scene()): + var inst = thing.instance() + if(inst.get_script()): + path = inst.get_script().get_path() + + gut.get_doubler().add_ignored_method(path, method_name) + +# ------------------------------------------------------------------------------ +# Stub something. +# +# Parameters +# 1: the thing to stub, a file path or a instance or a class +# 2: either an inner class subpath or the method name +# 3: the method name if an inner class subpath was specified +# NOTE: right now we cannot stub inner classes at the path level so this should +# only be called with two parameters. I did the work though so I'm going +# to leave it but not update the wiki. +# ------------------------------------------------------------------------------ +func stub(thing, p2, p3=null): + var method_name = p2 + var subpath = null + if(p3 != null): + subpath = p2 + method_name = p3 + var sp = _utils.StubParams.new(thing, method_name, subpath) + gut.get_stubber().add_stub(sp) + return sp + +# ------------------------------------------------------------------------------ +# convenience wrapper. +# ------------------------------------------------------------------------------ +func simulate(obj, times, delta): + gut.simulate(obj, times, delta) + +# ------------------------------------------------------------------------------ +# Replace the node at base_node.get_node(path) with with_this. All references +# to the node via $ and get_node(...) will now return with_this. with_this will +# get all the groups that the node that was replaced had. +# +# The node that was replaced is queued to be freed. +# +# TODO see replace_by method, this could simplify the logic here. +# ------------------------------------------------------------------------------ +func replace_node(base_node, path_or_node, with_this): + var path = path_or_node + + if(typeof(path_or_node) != TYPE_STRING): + # This will cause an engine error if it fails. It always returns a + # NodePath, even if it fails. Checking the name count is the only way + # I found to check if it found something or not (after it worked I + # didn't look any farther). + path = base_node.get_path_to(path_or_node) + if(path.get_name_count() == 0): + _lgr.error('You passed an object that base_node does not have. Cannot replace node.') + return + + if(!base_node.has_node(path)): + _lgr.error(str('Could not find node at path [', path, ']')) + return + + var to_replace = base_node.get_node(path) + var parent = to_replace.get_parent() + var replace_name = to_replace.get_name() + + parent.remove_child(to_replace) + parent.add_child(with_this) + with_this.set_name(replace_name) + with_this.set_owner(parent) + + var groups = to_replace.get_groups() + for i in range(groups.size()): + with_this.add_to_group(groups[i]) + + to_replace.queue_free() + + +# ------------------------------------------------------------------------------ +# This method does a somewhat complicated dance with Gut. It assumes that Gut +# will clear its parameter handler after it finishes calling a parameterized test +# enough times. +# ------------------------------------------------------------------------------ +func use_parameters(params): + var ph = gut.get_parameter_handler() + if(ph == null): + ph = _utils.ParameterHandler.new(params) + gut.set_parameter_handler(ph) + else: + _lgr.dec_indent() + + var output = str('(call #', ph.get_call_count() + 1, ') with paramters: ', ph.get_current_parameters()) + gut.p(output, 0, 0) + _lgr.inc_indent() + return ph.next_parameters() + +# ------------------------------------------------------------------------------ +# Marks whatever is passed in to be freed after the test finishes. It also +# returns what is passed in so you can save a line of code. +# var thing = autofree(Thing.new()) +# ------------------------------------------------------------------------------ +func autofree(thing): + gut.get_autofree().add_free(thing) + return thing + +# ------------------------------------------------------------------------------ +# Works the same as autofree except queue_free will be called on the object +# instead. This also imparts a brief pause after the test finishes so that +# the queued object has time to free. +# ------------------------------------------------------------------------------ +func autoqfree(thing): + gut.get_autofree().add_queue_free(thing) + return thing + +# ------------------------------------------------------------------------------ +# The same as autofree but it also adds the object as a child of the test. +# ------------------------------------------------------------------------------ +func add_child_autofree(node, legible_unique_name = false): + gut.get_autofree().add_free(node) + # Explicitly calling super here b/c add_child MIGHT change and I don't want + # a bug sneaking its way in here. + .add_child(node, legible_unique_name) + return node + +func add_child_autoqfree(node, legible_unique_name=false): + gut.get_autofree().add_queue_free(node) + # Explicitly calling super here b/c add_child MIGHT change and I don't want + # a bug sneaking its way in here. + .add_child(node, legible_unique_name) + return node diff --git a/test_proj/addons/gut/test_collector.gd b/test_proj/addons/gut/test_collector.gd new file mode 100644 index 0000000..964c057 --- /dev/null +++ b/test_proj/addons/gut/test_collector.gd @@ -0,0 +1,282 @@ +# ------------------------------------------------------------------------------ +# Used to keep track of info about each test ran. +# ------------------------------------------------------------------------------ +class Test: + # indicator if it passed or not. defaults to true since it takes only + # one failure to make it not pass. _fail in gut will set this. + var passed = true + # the name of the function + var name = "" + # flag to know if the name has been printed yet. + var has_printed_name = false + # the line number the test is on + var line_number = -1 + # the number of arguments the method has + var arg_count = 0 + +# ------------------------------------------------------------------------------ +# This holds all the meta information for a test script. It contains the +# name of the inner class and an array of Test "structs". +# +# This class also facilitates all the exporting and importing of tests. +# ------------------------------------------------------------------------------ +class TestScript: + var inner_class_name = null + var tests = [] + var path = null + var _utils = null + var _lgr = null + + func _init(utils=null, logger=null): + _utils = utils + _lgr = logger + + func to_s(): + var to_return = path + if(inner_class_name != null): + to_return += str('.', inner_class_name) + to_return += "\n" + for i in range(tests.size()): + to_return += str(' ', tests[i].name, "\n") + return to_return + + func get_new(): + return load_script().new() + + func load_script(): + #print('loading: ', get_full_name()) + var to_return = load(path) + if(inner_class_name != null): + # If we wanted to do inner classes in inner classses + # then this would have to become some kind of loop or recursive + # call to go all the way down the chain or this class would + # have to change to hold onto the loaded class instead of + # just path information. + to_return = to_return.get(inner_class_name) + return to_return + + func get_filename_and_inner(): + var to_return = get_filename() + if(inner_class_name != null): + to_return += '.' + inner_class_name + return to_return + + func get_full_name(): + var to_return = path + if(inner_class_name != null): + to_return += '.' + inner_class_name + return to_return + + func get_filename(): + return path.get_file() + + func has_inner_class(): + return inner_class_name != null + + # Note: although this no longer needs to export the inner_class names since + # they are pulled from metadata now, it is easier to leave that in + # so we don't have to cut the export down to unique script names. + func export_to(config_file, section): + config_file.set_value(section, 'path', path) + config_file.set_value(section, 'inner_class', inner_class_name) + var names = [] + for i in range(tests.size()): + names.append(tests[i].name) + config_file.set_value(section, 'tests', names) + + func _remap_path(source_path): + var to_return = source_path + if(!_utils.file_exists(source_path)): + _lgr.debug('Checking for remap for: ' + source_path) + var remap_path = source_path.get_basename() + '.gd.remap' + if(_utils.file_exists(remap_path)): + var cf = ConfigFile.new() + cf.load(remap_path) + to_return = cf.get_value('remap', 'path') + else: + _lgr.warn('Could not find remap file ' + remap_path) + return to_return + + func import_from(config_file, section): + path = config_file.get_value(section, 'path') + path = _remap_path(path) + # Null is an acceptable value, but you can't pass null as a default to + # get_value since it thinks you didn't send a default...then it spits + # out red text. This works around that. + var inner_name = config_file.get_value(section, 'inner_class', 'Placeholder') + if(inner_name != 'Placeholder'): + inner_class_name = inner_name + else: # just being explicit + inner_class_name = null + + func get_test_named(name): + return _utils.search_array(tests, 'name', name) + +# ------------------------------------------------------------------------------ +# start test_collector, I don't think I like the name. +# ------------------------------------------------------------------------------ +var scripts = [] +var _test_prefix = 'test_' +var _test_class_prefix = 'Test' + +var _utils = load('res://addons/gut/utils.gd').get_instance() +var _lgr = _utils.get_logger() + +func _does_inherit_from_test(thing): + var base_script = thing.get_base_script() + var to_return = false + if(base_script != null): + var base_path = base_script.get_path() + if(base_path == 'res://addons/gut/test.gd'): + to_return = true + else: + to_return = _does_inherit_from_test(base_script) + return to_return + +func _populate_tests(test_script): + var methods = test_script.load_script().get_script_method_list() + for i in range(methods.size()): + var name = methods[i]['name'] + if(name.begins_with(_test_prefix)): + var t = Test.new() + t.name = name + t.arg_count = methods[i]['args'].size() + test_script.tests.append(t) + +func _get_inner_test_class_names(loaded): + var inner_classes = [] + var const_map = loaded.get_script_constant_map() + for key in const_map: + var thing = const_map[key] + if(typeof(thing) == TYPE_OBJECT): + if(key.begins_with(_test_class_prefix)): + if(_does_inherit_from_test(thing)): + inner_classes.append(key) + else: + _lgr.warn(str('Ignoring Inner Class ', key, + ' because it does not extend res://addons/gut/test.gd')) + + # This could go deeper and find inner classes within inner classes + # but requires more experimentation. Right now I'm keeping it at + # one level since that is what the previous version did and there + # has been no demand for deeper nesting. + # _populate_inner_test_classes(thing) + return inner_classes + +func _parse_script(test_script): + var inner_classes = [] + var scripts_found = [] + + var loaded = load(test_script.path) + if(_does_inherit_from_test(loaded)): + _populate_tests(test_script) + scripts_found.append(test_script.path) + inner_classes = _get_inner_test_class_names(loaded) + + for i in range(inner_classes.size()): + var loaded_inner = loaded.get(inner_classes[i]) + if(_does_inherit_from_test(loaded_inner)): + var ts = TestScript.new(_utils, _lgr) + ts.path = test_script.path + ts.inner_class_name = inner_classes[i] + _populate_tests(ts) + scripts.append(ts) + scripts_found.append(test_script.path + '[' + inner_classes[i] +']') + + return scripts_found + +# ----------------- +# Public +# ----------------- +func add_script(path): + # SHORTCIRCUIT + if(has_script(path)): + return [] + + var f = File.new() + # SHORTCIRCUIT + if(!f.file_exists(path)): + _lgr.error('Could not find script: ' + path) + return + + var ts = TestScript.new(_utils, _lgr) + ts.path = path + scripts.append(ts) + return _parse_script(ts) + +func clear(): + scripts.clear() + +func has_script(path): + var found = false + var idx = 0 + while(idx < scripts.size() and !found): + if(scripts[idx].get_full_name() == path): + found = true + else: + idx += 1 + return found + +func export_tests(path): + var success = true + var f = ConfigFile.new() + for i in range(scripts.size()): + scripts[i].export_to(f, str('TestScript-', i)) + var result = f.save(path) + if(result != OK): + _lgr.error(str('Could not save exported tests to [', path, ']. Error code: ', result)) + success = false + return success + +func import_tests(path): + var success = false + var f = ConfigFile.new() + var result = f.load(path) + if(result != OK): + _lgr.error(str('Could not load exported tests from [', path, ']. Error code: ', result)) + else: + var sections = f.get_sections() + for key in sections: + var ts = TestScript.new(_utils, _lgr) + ts.import_from(f, key) + _populate_tests(ts) + scripts.append(ts) + success = true + return success + +func get_script_named(name): + return _utils.search_array(scripts, 'get_filename_and_inner', name) + +func get_test_named(script_name, test_name): + var s = get_script_named(script_name) + if(s != null): + return s.get_test_named(test_name) + else: + return null + +func to_s(): + var to_return = '' + for i in range(scripts.size()): + to_return += scripts[i].to_s() + "\n" + return to_return + +# --------------------- +# Accessors +# --------------------- +func get_logger(): + return _lgr + +func set_logger(logger): + _lgr = logger + +func get_test_prefix(): + return _test_prefix + +func set_test_prefix(test_prefix): + _test_prefix = test_prefix + +func get_test_class_prefix(): + return _test_class_prefix + +func set_test_class_prefix(test_class_prefix): + _test_class_prefix = test_class_prefix diff --git a/test_proj/addons/gut/thing_counter.gd b/test_proj/addons/gut/thing_counter.gd new file mode 100644 index 0000000..a9b0b48 --- /dev/null +++ b/test_proj/addons/gut/thing_counter.gd @@ -0,0 +1,43 @@ +var things = {} + +func get_unique_count(): + return things.size() + +func add(thing): + if(things.has(thing)): + things[thing] += 1 + else: + things[thing] = 1 + +func has(thing): + return things.has(thing) + +func get(thing): + var to_return = 0 + if(things.has(thing)): + to_return = things[thing] + return to_return + +func sum(): + var count = 0 + for key in things: + count += things[key] + return count + +func to_s(): + var to_return = "" + for key in things: + to_return += str(key, ": ", things[key], "\n") + to_return += str("sum: ", sum()) + return to_return + +func get_max_count(): + var max_val = null + for key in things: + if(max_val == null or things[key] > max_val): + max_val = things[key] + return max_val + +func add_array_items(array): + for i in range(array.size()): + add(array[i]) diff --git a/test_proj/addons/gut/utils.gd b/test_proj/addons/gut/utils.gd new file mode 100644 index 0000000..1839f7f --- /dev/null +++ b/test_proj/addons/gut/utils.gd @@ -0,0 +1,312 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# Description +# ----------- +# This class is a PSUEDO SINGLETON. You should not make instances of it but use +# the get_instance static method. +# ############################################################################## +extends Node + +# ------------------------------------------------------------------------------ +# The instance name as a function since you can't have static variables. +# ------------------------------------------------------------------------------ +static func INSTANCE_NAME(): + return '__GutUtilsInstName__' + +# ------------------------------------------------------------------------------ +# Gets the root node without having to be in the tree and pushing out an error +# if we don't have a main loop ready to go yet. +# ------------------------------------------------------------------------------ +static func get_root_node(): + var to_return = null + var main_loop = Engine.get_main_loop() + if(main_loop != null): + return main_loop.root + else: + push_error('No Main Loop Yet') + return null + +# ------------------------------------------------------------------------------ +# Get the ONE instance of utils +# ------------------------------------------------------------------------------ +static func get_instance(): + var the_root = get_root_node() + var inst = null + if(the_root.has_node(INSTANCE_NAME())): + inst = the_root.get_node(INSTANCE_NAME()) + else: + inst = load('res://addons/gut/utils.gd').new() + inst.set_name(INSTANCE_NAME()) + the_root.add_child(inst) + return inst + +var Logger = load('res://addons/gut/logger.gd') # everything should use get_logger +var _lgr = null + +var _test_mode = false +var AutoFree = load('res://addons/gut/autofree.gd') +var Doubler = load('res://addons/gut/doubler.gd') +var Gut = load('res://addons/gut/gut.gd') +var HookScript = load('res://addons/gut/hook_script.gd') +var MethodMaker = load('res://addons/gut/method_maker.gd') +var OneToMany = load('res://addons/gut/one_to_many.gd') +var OrphanCounter = load('res://addons/gut/orphan_counter.gd') +var ParameterFactory = load('res://addons/gut/parameter_factory.gd') +var ParameterHandler = load('res://addons/gut/parameter_handler.gd') +var Printers = load('res://addons/gut/printers.gd') +var Spy = load('res://addons/gut/spy.gd') +var Strutils = load('res://addons/gut/strutils.gd') +var Stubber = load('res://addons/gut/stubber.gd') +var StubParams = load('res://addons/gut/stub_params.gd') +var Summary = load('res://addons/gut/summary.gd') +var Test = load('res://addons/gut/test.gd') +var TestCollector = load('res://addons/gut/test_collector.gd') +var ThingCounter = load('res://addons/gut/thing_counter.gd') + +# Source of truth for the GUT version +var version = '7.0.0' +# The required Godot version as an array. +var req_godot = [3, 2, 0] +# Used for doing file manipulation stuff so as to not keep making File instances. +# could be a bit of overkill but who cares. +var _file_checker = File.new() + +const GUT_METADATA = '__gut_metadata_' + +enum DOUBLE_STRATEGY{ + FULL, + PARTIAL +} + +# ------------------------------------------------------------------------------ +# Blurb of text with GUT and Godot versions. +# ------------------------------------------------------------------------------ +func get_version_text(): + var v_info = Engine.get_version_info() + var gut_version_info = str('GUT version: ', version) + var godot_version_info = str('Godot version: ', v_info.major, '.', v_info.minor, '.', v_info.patch) + return godot_version_info + "\n" + gut_version_info + + +# ------------------------------------------------------------------------------ +# Returns a nice string for erroring out when we have a bad Godot version. +# ------------------------------------------------------------------------------ +func get_bad_version_text(): + var ver = join_array(req_godot, '.') + var info = Engine.get_version_info() + var gd_version = str(info.major, '.', info.minor, '.', info.patch) + return 'GUT ' + version + ' requires Godot ' + ver + ' or greater. Godot version is ' + gd_version + + +# ------------------------------------------------------------------------------ +# Checks the Godot version against req_godot array. +# ------------------------------------------------------------------------------ +func is_version_ok(): + var info = Engine.get_version_info() + var is_ok = info.major >= req_godot[0] and \ + info.minor >= req_godot[1] and \ + info.patch >= req_godot[2] + return is_ok + + +# ------------------------------------------------------------------------------ +# Everything should get a logger through this. +# +# When running in test mode this will always return a new logger so that errors +# are not caused by getting bad warn/error/etc counts. +# ------------------------------------------------------------------------------ +func get_logger(): + if(_test_mode): + return Logger.new() + else: + if(_lgr == null): + _lgr = Logger.new() + return _lgr + + +# ------------------------------------------------------------------------------ +# Returns an array created by splitting the string by the delimiter +# ------------------------------------------------------------------------------ +func split_string(to_split, delim): + var to_return = [] + + var loc = to_split.find(delim) + while(loc != -1): + to_return.append(to_split.substr(0, loc)) + to_split = to_split.substr(loc + 1, to_split.length() - loc) + loc = to_split.find(delim) + to_return.append(to_split) + return to_return + + +# ------------------------------------------------------------------------------ +# Returns a string containing all the elements in the array separated by delim +# ------------------------------------------------------------------------------ +func join_array(a, delim): + var to_return = '' + for i in range(a.size()): + to_return += str(a[i]) + if(i != a.size() -1): + to_return += str(delim) + return to_return + + +# ------------------------------------------------------------------------------ +# return if_null if value is null otherwise return value +# ------------------------------------------------------------------------------ +func nvl(value, if_null): + if(value == null): + return if_null + else: + return value + + +# ------------------------------------------------------------------------------ +# returns true if the object has been freed, false if not +# +# From what i've read, the weakref approach should work. It seems to work most +# of the time but sometimes it does not catch it. The str comparison seems to +# fill in the gaps. I've not seen any errors after adding that check. +# ------------------------------------------------------------------------------ +func is_freed(obj): + var wr = weakref(obj) + return !(wr.get_ref() and str(obj) != '[Deleted Object]') + + +# ------------------------------------------------------------------------------ +# Pretty self explanitory. +# ------------------------------------------------------------------------------ +func is_not_freed(obj): + return !is_freed(obj) + + +# ------------------------------------------------------------------------------ +# Checks if the passed in object is a GUT Double or Partial Double. +# ------------------------------------------------------------------------------ +func is_double(obj): + var to_return = false + if(typeof(obj) == TYPE_OBJECT and is_instance_valid(obj)): + to_return = obj.has_method('__gut_instance_from_id') + return to_return + + +# ------------------------------------------------------------------------------ +# Returns an array of values by calling get(property) on each element in source +# ------------------------------------------------------------------------------ +func extract_property_from_array(source, property): + var to_return = [] + for i in (source.size()): + to_return.append(source[i].get(property)) + return to_return + + +# ------------------------------------------------------------------------------ +# true if file exists, false if not. +# ------------------------------------------------------------------------------ +func file_exists(path): + return _file_checker.file_exists(path) + + +# ------------------------------------------------------------------------------ +# Write a file. +# ------------------------------------------------------------------------------ +func write_file(path, content): + var f = File.new() + var result = f.open(path, f.WRITE) + if(result == OK): + f.store_string(content) + f.close() + + +# ------------------------------------------------------------------------------ +# true if what is passed in is null or an empty string. +# ------------------------------------------------------------------------------ +func is_null_or_empty(text): + return text == null or text == '' + + +# ------------------------------------------------------------------------------ +# Get the name of a native class or null if the object passed in is not a +# native class. +# ------------------------------------------------------------------------------ +func get_native_class_name(thing): + var to_return = null + if(is_native_class(thing)): + to_return = thing.new().get_class() + return to_return + + +# ------------------------------------------------------------------------------ +# Checks an object to see if it is a GDScriptNativeClass +# ------------------------------------------------------------------------------ +func is_native_class(thing): + var it_is = false + if(typeof(thing) == TYPE_OBJECT): + it_is = str(thing).begins_with("[GDScriptNativeClass:") + return it_is + + +# ------------------------------------------------------------------------------ +# Returns the text of a file or an empty string if the file could not be opened. +# ------------------------------------------------------------------------------ +func get_file_as_text(path): + var to_return = '' + var f = File.new() + var result = f.open(path, f.READ) + if(result == OK): + to_return = f.get_as_text() + f.close() + return to_return + + +# ------------------------------------------------------------------------------ +# Loops through an array of things and calls a method or checks a property on +# each element until it finds the returned value. The item in the array is +# returned or null if it is not found. +# ------------------------------------------------------------------------------ +func search_array(ar, prop_method, value): + var found = false + var idx = 0 + + while(idx < ar.size() and !found): + var item = ar[idx] + if(item.get(prop_method) != null): + if(item.get(prop_method) == value): + found = true + elif(item.has_method(prop_method)): + if(item.call(prop_method) == value): + found = true + + if(!found): + idx += 1 + + if(found): + return ar[idx] + else: + return null \ No newline at end of file diff --git a/test_proj/default_env.tres b/test_proj/default_env.tres new file mode 100644 index 0000000..20207a4 --- /dev/null +++ b/test_proj/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +[resource] +background_mode = 2 +background_sky = SubResource( 1 ) diff --git a/test_proj/icon.png b/test_proj/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c98fbb601c83c81ec8c22b1dba7d1d57c62b323c GIT binary patch literal 3305 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 literal 0 HcmV?d00001 diff --git a/test_proj/icon.png.import b/test_proj/icon.png.import new file mode 100644 index 0000000..96cbf46 --- /dev/null +++ b/test_proj/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.png" +dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/test_proj/project.godot b/test_proj/project.godot new file mode 100644 index 0000000..f055d76 --- /dev/null +++ b/test_proj/project.godot @@ -0,0 +1,28 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ ] +_global_script_class_icons={ + +} + +[application] + +config/name="test_proj" +run/main_scene="res://Gut.tscn" +config/icon="res://icon.png" + +[editor_plugins] + +enabled=PoolStringArray( "gut" ) + +[rendering] + +environment/default_environment="res://default_env.tres" diff --git a/test_proj/tests/test_test.gd b/test_proj/tests/test_test.gd new file mode 100644 index 0000000..c500f3c --- /dev/null +++ b/test_proj/tests/test_test.gd @@ -0,0 +1,4 @@ +extends "res://addons/gut/test.gd" + +func test_test(): + assert_eq(1,1)