diff options
Diffstat (limited to 'lang/js/src/Message.js')
-rw-r--r-- | lang/js/src/Message.js | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js new file mode 100644 index 00000000..b83caf6d --- /dev/null +++ b/lang/js/src/Message.js @@ -0,0 +1,239 @@ +/* gpgme.js - Javascript integration for gpgme + * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik + * + * This file is part of GPGME. + * + * GPGME is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * GPGME is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ + * + * Author(s): + * Maximilian Krambach <[email protected]> + */ + +import { permittedOperations } from './permittedOperations'; +import { gpgme_error } from './Errors'; +import { Connection } from './Connection'; + +/** + * Initializes a message for gnupg, validating the message's purpose with + * {@link permittedOperations} first + * @param {String} operation + * @returns {GPGME_Message} The Message object + */ +export function createMessage (operation){ + if (typeof (operation) !== 'string'){ + throw gpgme_error('PARAM_WRONG'); + } + if (permittedOperations.hasOwnProperty(operation)){ + return new GPGME_Message(operation); + } else { + throw gpgme_error('MSG_WRONG_OP'); + } +} + +/** + * A Message collects, validates and handles all information required to + * successfully establish a meaningful communication with gpgme-json via + * {@link Connection.post}. The definition on which communication is available + * can be found in {@link permittedOperations}. + * @class + */ +export class GPGME_Message { + + constructor (operation){ + this._msg = { + op: operation, + chunksize: 1023* 1024 + }; + this._expected = null; + } + + get operation (){ + return this._msg.op; + } + + set expected (value){ + if (value === 'base64'){ + this._expected = value; + } + } + + get expected () { + return this._expected; + } + /** + * The maximum size of responses from gpgme in bytes. As of July 2018, + * most browsers will only accept answers up to 1 MB of size. + * Everything above that threshold will not pass through + * nativeMessaging; answers that are larger need to be sent in parts. + * The lower limit is set to 10 KB. Messages smaller than the threshold + * will not encounter problems, larger messages will be received in + * chunks. If the value is not explicitly specified, 1023 KB is used. + */ + set chunksize (value){ + if ( + Number.isInteger(value) && + value > 10 * 1024 && + value <= 1024 * 1024 + ){ + this._msg.chunksize = value; + } + } + + get chunksize (){ + return this._msg.chunksize; + } + + /** + * Returns the prepared message with parameters and completeness checked + * @returns {Object|null} Object to be posted to gnupg, or null if + * incomplete + */ + get message () { + if (this.isComplete() === true){ + return this._msg; + } else { + return null; + } + } + + /** + * Sets a parameter for the message. It validates with + * {@link permittedOperations} + * @param {String} param Parameter to set + * @param {any} value Value to set + * @returns {Boolean} If the parameter was set successfully + */ + setParameter ( param,value ){ + if (!param || typeof (param) !== 'string'){ + throw gpgme_error('PARAM_WRONG'); + } + let po = permittedOperations[this._msg.op]; + if (!po){ + throw gpgme_error('MSG_WRONG_OP'); + } + let poparam = null; + if (po.required.hasOwnProperty(param)){ + poparam = po.required[param]; + } else if (po.optional.hasOwnProperty(param)){ + poparam = po.optional[param]; + } else { + throw gpgme_error('PARAM_WRONG'); + } + // check incoming value for correctness + let checktype = function (val){ + switch (typeof (val)){ + case 'string': + if (poparam.allowed.indexOf(typeof (val)) >= 0 + && val.length > 0) { + return true; + } + throw gpgme_error('PARAM_WRONG'); + case 'number': + if ( + poparam.allowed.indexOf('number') >= 0 + && isNaN(value) === false){ + return true; + } + throw gpgme_error('PARAM_WRONG'); + + case 'boolean': + if (poparam.allowed.indexOf('boolean') >= 0){ + return true; + } + throw gpgme_error('PARAM_WRONG'); + case 'object': + if (Array.isArray(val)){ + if (poparam.array_allowed !== true){ + throw gpgme_error('PARAM_WRONG'); + } + for (let i=0; i < val.length; i++){ + let res = checktype(val[i]); + if (res !== true){ + return res; + } + } + if (val.length > 0) { + return true; + } + } else if (val instanceof Uint8Array){ + if (poparam.allowed.indexOf('Uint8Array') >= 0){ + return true; + } + throw gpgme_error('PARAM_WRONG'); + } else { + throw gpgme_error('PARAM_WRONG'); + } + break; + default: + throw gpgme_error('PARAM_WRONG'); + } + }; + let typechecked = checktype(value); + if (typechecked !== true){ + return typechecked; + } + if (poparam.hasOwnProperty('allowed_data')){ + if (poparam.allowed_data.indexOf(value) < 0){ + return gpgme_error('PARAM_WRONG'); + } + } + this._msg[param] = value; + return true; + } + + + /** + * Check if the message has the minimum requirements to be sent, that is + * all 'required' parameters according to {@link permittedOperations}. + * @returns {Boolean} true if message is complete. + */ + isComplete (){ + if (!this._msg.op){ + return false; + } + let reqParams = Object.keys( + permittedOperations[this._msg.op].required); + let msg_params = Object.keys(this._msg); + for (let i=0; i < reqParams.length; i++){ + if (msg_params.indexOf(reqParams[i]) < 0){ + return false; + } + } + return true; + } + /** + * Sends the Message via nativeMessaging and resolves with the answer. + * @returns {Promise<Object|GPGME_Error>} + * @async + */ + post (){ + let me = this; + return new Promise(function (resolve, reject) { + if (me.isComplete() === true) { + + let conn = new Connection; + conn.post(me).then(function (response) { + resolve(response); + }, function (reason) { + reject(reason); + }); + } + else { + reject(gpgme_error('MSG_INCOMPLETE')); + } + }); + } + +} |