From c72adc00965fe4fcedd9d18609211021a091b28b Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 25 Apr 2018 10:54:24 +0200 Subject: [PATCH] js: change in Error behaviour -- * Error objects will now return the error code if defined as error type in src/Errors.js, or do a console.log if it is a warning. Errors from the native gpgme-json will be marked as GNUPG_ERROR. --- lang/js/src/Connection.js | 58 ++++++----- lang/js/src/Errors.js | 170 +++++++++++++------------------ lang/js/src/Helpers.js | 16 +-- lang/js/src/Key.js | 2 +- lang/js/src/Message.js | 12 +-- lang/js/src/gpgmejs.js | 13 +-- lang/js/src/gpgmejs_openpgpjs.js | 14 +-- lang/js/src/index.js | 3 +- 8 files changed, 132 insertions(+), 156 deletions(-) diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js index e6ff67be..8bc3d42a 100644 --- a/lang/js/src/Connection.js +++ b/lang/js/src/Connection.js @@ -1,5 +1,3 @@ -import { GPGME_Message } from "./Message"; - /* gpgme.js - Javascript integration for gpgme * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * @@ -26,7 +24,8 @@ import { GPGME_Message } from "./Message"; * expected. */ import { permittedOperations } from './permittedOperations' -import { GPGMEJS_Error} from "./Errors" +import { GPGMEJS_Error } from "./Errors" +import { GPGME_Message } from "./Message"; /** * A Connection handles the nativeMessaging interaction. @@ -60,20 +59,20 @@ export class Connection{ /** * Opens a nativeMessaging port. - * TODO: Error handling ALREADY_CONNECTED */ connect(){ if (this._isConnected === true){ - return new GPGMEJS_Error('ALREADY_CONNECTED'); + GPGMEJS_Error('CONN_ALREADY_CONNECTED'); + } else { + this._isConnected = true; + this._connection = chrome.runtime.connectNative('gpgmejson'); + let me = this; + this._connection.onDisconnect.addListener( + function(){ + me._isConnected = false; + } + ); } - this._isConnected = true; - this._connection = chrome.runtime.connectNative('gpgmejson'); - let me = this; - this._connection.onDisconnect.addListener( - function(){ - me._isConnected = false; - } - ); } /** @@ -84,28 +83,31 @@ export class Connection{ */ post(message){ if (!this.isConnected){ - return Promise.reject(new GPGMEJS_Error('NO_CONNECT')); + return Promise.reject(GPGMEJS_Error('CONN_NO_CONNECT')); } if (!message || !message instanceof GPGME_Message){ - return Promise.reject(new GPGMEJS_Error('WRONGPARAM')); + return Promise.reject(GPGMEJS_Error('PARAM_WRONG'), message); } if (message.isComplete !== true){ - return Promise.reject(new GPGMEJS_Error('MSG_INCOMPLETE')); + return Promise.reject(GPGMEJS_Error('MSG_INCOMPLETE')); } - // let timeout = 5000; //TODO config let me = this; return new Promise(function(resolve, reject){ let answer = new Answer(message.operation); let listener = function(msg) { if (!msg){ me._connection.onMessage.removeListener(listener) - reject(new GPGMEJS_Error('EMPTY_GPG_ANSWER')); + reject(GPGMEJS_Error('CONN_EMPTY_GPG_ANSWER')); } else if (msg.type === "error"){ me._connection.onMessage.removeListener(listener) - //TODO: GPGMEJS_Error? - reject(msg.msg); + reject( + {code: 'GNUPG_ERROR', + msg: msg.msg} ); } else { - answer.add(msg); + let answer_result = answer.add(msg); + if (answer_result !== true){ + reject(answer_result); + } if (msg.more === true){ me._connection.postMessage({'op': 'getmore'}); } else { @@ -117,11 +119,12 @@ export class Connection{ me._connection.onMessage.addListener(listener); me._connection.postMessage(message.message); + //TBD: needs to be aware if there is a pinentry pending // setTimeout( // function(){ // me.disconnect(); - // reject(new GPGMEJS_Error('TIMEOUT', 5000)); + // reject(GPGMEJS_Error('CONN_TIMEOUT')); // }, timeout); }); } @@ -141,6 +144,7 @@ class Answer{ /** * Add the information to the answer * @param {Object} msg The message as received with nativeMessaging + * returns true if successfull, GPGMEJS_Error otherwise */ add(msg){ if (this._response === undefined){ @@ -148,12 +152,15 @@ class Answer{ } let messageKeys = Object.keys(msg); let poa = permittedOperations[this.operation].answer; + if (messageKeys.length === 0){ + return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER'); + } for (let i= 0; i < messageKeys.length; i++){ let key = messageKeys[i]; switch (key) { case 'type': if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){ - return new GPGMEJS_Error('UNEXPECTED_ANSWER'); + return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER'); } break; case 'more': @@ -172,7 +179,7 @@ class Answer{ this._response[key] = msg[key]; } else if (this._response[key] !== msg[key]){ - return new GPGMEJS_Error('UNEXPECTED_ANSWER',msg[key]); + return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER',msg[key]); } } //infos may be json objects etc. Not yet defined. @@ -184,11 +191,12 @@ class Answer{ this._response.push(msg[key]); } else { - return new GPGMEJS_Error('UNEXPECTED_ANSWER', key); + return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER', key); } break; } } + return true; } /** diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js index c2356f7c..c49bfe21 100644 --- a/lang/js/src/Errors.js +++ b/lang/js/src/Errors.js @@ -18,131 +18,99 @@ * SPDX-License-Identifier: LGPL-2.1+ */ -// This is a preliminary collection of erors and warnings to be thrown and implemented. - -// general idea: if throw , throw the NAME -// return false || 'return' property - -//TODO: Connection.NOCONNECT promise -//connection.timeout: Be aware of pinentry - -export class GPGMEJS_Error { - - constructor(code = 'GENERIC_ERROR', details){ - let config = { //TODO TEMP - debug: 'console', // |'alert' - throw: 'default' // | 'always' | 'never' - }; +/** + * Checks the given error code and returns some information about it's meaning + * @param {String} code The error code + * @returns {Object} An object containing string properties code and msg + * TODO: error-like objects with the code 'GNUPG_ERROR' are errors sent + * directly by gnupg as answer in Connection.post() + */ +export function GPGMEJS_Error(code = 'GENERIC_ERROR'){ + if (!typeof(code) === 'string'){ + code = 'GENERIC_ERROR'; + } let errors = { //TODO: someplace else - //Connection errors - 'ALREADY_CONNECTED':{ - msg: 'The connection was already established. The action would overwrite the context', - throw: true + // Connection + 'CONN_NO_CONNECT': { + msg:'Connection with the nativeMessaging host could not be' + + ' established.', + type: 'error' }, - 'NO_CONNECT': { - msg:'Connection with the nativeMessaging host could not be established.', - throw: true + 'CONN_EMPTY_GPG_ANSWER':{ + msg: 'The nativeMessaging answer was empty.', + type: 'error' }, - 'EMPTY_GPG_ANSWER':{ - msg: 'The nativeMesaging answer was empty', - throw: true + 'CONN_TIMEOUT': { + msg: 'A connection timeout was exceeded.', + type: 'error' }, - 'TIMEOUT': { - msg: 'A timeout was exceeded.', - throw: false + 'CONN_UNEXPECTED_ANSWER': { + msg: 'The answer from gnupg was not as expected.', + type: 'error' }, - - 'UNEXPECTED_ANSWER': { - msg: 'The answer from gnupg was not as expected', - throw: true - }, - - // Message/Data Errors - - 'NO_KEYS' : { - msg: 'There were no valid keys provided.', - throw: true - }, - 'NOT_A_FPR': { - msg: 'The String is not an accepted fingerprint', - throw: false + 'CONN_ALREADY_CONNECTED':{ + msg: 'A connection was already established.', + type: 'warn' }, + // Message/Data 'MSG_INCOMPLETE': { - msg: 'The Message did not match the minimum requirements for the interaction', - throw: true + msg: 'The Message did not match the minimum requirements for' + + ' the interaction.', + type: 'error' }, - 'EMPTY_MSG' : { - msg: 'The Message has no data.', - throw: true - }, - 'MSG_NODATA':{ - msg: 'The data sent is empty. This may be unintentional.', - throw: false + 'MSG_EMPTY' : { + msg: 'The Message is empty.', + type: 'error' }, 'MSG_OP_PENDING': { - msg: 'There is no operation specified yet. The parameter cannot be set', - throw: false + msg: 'There is no operation specified yet. The parameter cannot' + + ' be set', + type: 'warning' }, - 'WRONG_OP': { - msg: "The operation requested could not be found", - throw: true + 'MSG_WRONG_OP': { + msg: 'The operation requested could not be found', + type: 'warning' + }, + 'MSG_NO_KEYS' : { + msg: 'There were no valid keys provided.', + type: 'warn' + }, + 'MSG_NOT_A_FPR': { + msg: 'The String is not an accepted fingerprint', + type: 'warn' }, - //generic errors - - 'WRONGPARAM':{ + // generic + 'PARAM_WRONG':{ msg: 'invalid parameter was found', - throw: true - }, - 'WRONGTYPE':{ - msg: 'invalid parameter type was found', - throw: true + type: 'error' }, 'NOT_IMPLEMENTED': { msg: 'A openpgpjs parameter was submitted that is not implemented', - throw: true + type: 'error' + }, + 'NOT_YET_IMPLEMENTED': { + msg: 'Support of this is probable, but it is not implemented yet', + type: 'error' }, 'GENERIC_ERROR': { msg: 'Unspecified error', - throw: true + type: 'error' }, - - // hopefully temporary errors - - 'NOT_YET_IMPLEMENTED': { - msg: 'Support of this is probable, but it is not implemented yet', - throw: false - } } - if (!errors.hasOwnProperty(code)){ - throw('GENERIC_ERROR'); + if (code === 'TODO'){ + alert('TODO_Error!'); } - let msg = code; - if (errors[code].msg !== undefined){ - msg = msg + ': ' + errors[code].msg; + if (errors.hasOwnProperty(code)){ + code = 'GENERIC_ERROR'; } - if (details){ - msg = msg + ' ' + details; + if (error.type === 'error'){ + return {code: 'code', + msg: errors[code].msg + }; } - if (config.debug === 'console'){ - console.log(msg); - } else if (config.debug === 'alert'){ - alert(msg); + if (error.type === 'warning'){ + console.log(code + ': ' + error[code].msg); } - switch (config.throw) { - case 'default': - if (errors[code].throw === true){ - throw(code); - } - break; - case 'always': - throw(code); - break; - - case 'never': - break; - default: - throw('GENERIC_ERROR'); - } - } + return undefined; } diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js index 922ca06c..d9750ba7 100644 --- a/lang/js/src/Helpers.js +++ b/lang/js/src/Helpers.js @@ -1,5 +1,3 @@ -import { GPGMEJS_Error } from "./Errors"; - /* gpgme.js - Javascript integration for gpgme * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * @@ -19,18 +17,19 @@ import { GPGMEJS_Error } from "./Errors"; * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ +import { GPGMEJS_Error } from "./Errors"; /** * Tries to return an array of fingerprints, either from input fingerprints or * from Key objects * @param {Key |Array| GPGME_Key | Array|String|Array} input - * @param {Boolean} nocheck if set, an empty result is acceptable * @returns {Array} Array of fingerprints. */ export function toKeyIdArray(input, nocheck){ if (!input){ - return (nocheck ===true)? [] : new GPGMEJS_Error('NO_KEYS'); + GPGMEJS_Error('MSG_NO_KEYS'); + return []; } if (!Array.isArray(input)){ input = [input]; @@ -41,7 +40,7 @@ export function toKeyIdArray(input, nocheck){ if (isFingerprint(input[i]) === true){ result.push(input[i]); } else { - GPGMEJS_Error + GPGMEJS_Error('MSG_NOT_A_FPR'); } } else if (typeof(input[i]) === 'object'){ let fpr = ''; @@ -53,13 +52,16 @@ export function toKeyIdArray(input, nocheck){ } if (isFingerprint(fpr) === true){ result.push(fpr); + } else { + GPGMEJS_Error('MSG_NOT_A_FPR'); } } else { - return new GPGMEJS_Error('WRONGTYPE'); + return GPGMEJS_Error('PARAM_WRONG'); } } if (result.length === 0){ - return (nocheck===true)? [] : new GPGMEJS_Error('NO_KEYS'); + GPGMEJS_Error('MSG_NO_KEYS'); + return []; } else { return result; } diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index f59b9901..5ae80438 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -172,7 +172,7 @@ export class GPGME_Key { * */ function checkKey(fingerprint, property){ - return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED')); + return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED')); return new Promise(function(resolve, reject){ if (!isFingerprint(fingerprint)){ diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js index f5e21e00..1b36f11d 100644 --- a/lang/js/src/Message.js +++ b/lang/js/src/Message.js @@ -39,20 +39,20 @@ export class GPGME_Message { */ setParameter(param,value){ if (!param || typeof(param) !== 'string'){ - return new GPGMEJS_Error('WRONGPARAM', 'type check failed'); + return GPGMEJS_Error('PARAM_WRONG'); } if (!this._msg || !this._msg.op){ - return new GPGMEJS_Error('MSG_OP_PENDING'); + return GPGMEJS_Error('MSG_OP_PENDING'); } let po = permittedOperations[this._msg.op]; if (!po){ - return new GPGMEJS_Error('WRONG_OP', param); + return GPGMEJS_Error('MSG_WRONG_OP'); } if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){ this._msg[param] = value; return true; } - return new GPGMEJS_Error('WRONGPARAM', param); + return GPGMEJS_Error('PARAM_WRONG'); } /** @@ -98,7 +98,7 @@ export class GPGME_Message { */ function setOperation (scope, operation){ if (!operation || typeof(operation) !== 'string'){ - return new GPGMEJS_Error('WRONGTYPE'); + return GPGMEJS_Error('PARAM_WRONG'); } if (permittedOperations.hasOwnProperty(operation)){ if (!scope._msg){ @@ -106,6 +106,6 @@ function setOperation (scope, operation){ } scope._msg.op = operation; } else { - return new GPGMEJS_Error('WRONG_OP'); + return GPGMEJS_Error('MSG_WRONG_OP'); } } \ No newline at end of file diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js index 03ed5cb6..b20ff0f2 100644 --- a/lang/js/src/gpgmejs.js +++ b/lang/js/src/gpgmejs.js @@ -95,9 +95,8 @@ export class GpgME { */ decrypt(data){ - if (data === undefined){ - return Promise.reject(new GPGMEJS_Error ('EMPTY_MSG')); + return Promise.reject(GPGMEJS_Error('MSG_EMPTY')); } let msg = new GPGME_Message('decrypt'); putData(msg, data); @@ -106,7 +105,7 @@ export class GpgME { } deleteKey(key, delete_secret = false, no_confirm = false){ - return Promise.reject(new GPGMEJS_Error ('NOT_YET_IMPLEMENTED')); + return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED')); let msg = new GPGME_Message('deletekey'); let key_arr = toKeyIdArray(key); if (key_arr.length !== 1){ @@ -127,7 +126,7 @@ export class GpgME { case 'ERR_NO_ERROR': return Promise.resolve('okay'); //TBD default: - return Promise.reject(new GPGMEJS_Error); + return Promise.reject(GPGMEJS_Error('TODO') ); // // INV_VALUE, // GPG_ERR_NO_PUBKEY, // GPG_ERR_AMBIGUOUS_NAME, @@ -146,11 +145,9 @@ export class GpgME { */ function putData(message, data){ if (!message || !message instanceof GPGME_Message ) { - return new GPGMEJS_Error('WRONGPARAM'); + return GPGMEJS_Error('PARAM_WRONG'); } if (!data){ - //TODO Debug only! No data is legitimate - console.log('Warning. no data in message'); message.setParameter('data', ''); } else if (data instanceof Uint8Array){ let decoder = new TextDecoder('utf8'); @@ -167,6 +164,6 @@ function putData(message, data){ message.setParameter ('data', decoder.decode(txt)); } } else { - return new GPGMEJS_Error('WRONGPARAM'); + return GPGMEJS_Error('PARAM_WRONG'); } } diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js index 23076569..e32f43a3 100644 --- a/lang/js/src/gpgmejs_openpgpjs.js +++ b/lang/js/src/gpgmejs_openpgpjs.js @@ -88,14 +88,14 @@ || signature !== null || returnSessionKey !== null || date !== null){ - return Promise.reject(new GPMGEJS_Error('NOT_IMPLEMENTED')); + return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED')); } if ( privateKeys || filename || compression || armor === false || detached == true){ - return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED')); + return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED')); } return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard); } @@ -123,14 +123,14 @@ if (passwords !== undefined || sessionKeys || date){ - return Promise.reject(new GPGMEJS_Error('NOT_IMPLEMENTED')); + return Promise.reject(GPGMEJS_Error('NOT_IMPLEMENTED')); } if ( privateKeys || publicKeys || format !== 'utf8' || signature ){ - return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED')); + return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED')); } return this.GpgME.decrypt(message); // TODO: translate between: @@ -185,7 +185,7 @@ class GPGME_Keyring_openpgpmode { else { // TODO: Can there be "no default key"? // TODO: Can there be several default keys? - return new GPGMEJS_Error; //TODO + return GPGMEJS_Error('TODO'); } }); } @@ -202,10 +202,10 @@ class GPGME_Keyring_openpgpmode { */ deleteKey(key){ if (typeof(key) !== "object"){ - return Promise.reject(new GPGMEJS_Error('WRONGPARAM')); + return Promise.reject(GPGMEJS_Error('PARAM_WRONG')); } if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){ - return Promise.reject(new GPGMEJS_Error('WRONGPARAM')); + return Promise.reject(GPGMEJS_Error('PARAM_WRONG')); } let key_to_delete = new GPGME_Key(key.fingerprint); return key_to_delete.deleteKey(key.secret); diff --git a/lang/js/src/index.js b/lang/js/src/index.js index 0cb2301c..a54277c2 100644 --- a/lang/js/src/index.js +++ b/lang/js/src/index.js @@ -19,6 +19,7 @@ */ import { GpgME } from "./gpgmejs"; +import { GPGMEJS_Error } from "./Errors"; import { GpgME_openpgpmode } from "./gpgmejs_openpgpjs"; import { Connection } from "./Connection"; @@ -45,7 +46,7 @@ function init( config = { resolve(new GpgME(connection)); } } else { - reject('NO_CONNECT'); + reject(GPGMEJS_Error('CONN_NO_CONNECT')); } }; setTimeout(delayedreaction, 5);