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.
This commit is contained in:
Maximilian Krambach 2018-04-25 10:54:24 +02:00
parent 30c47d80a2
commit c72adc0096
8 changed files with 132 additions and 156 deletions

View File

@ -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
*
@ -27,6 +25,7 @@ import { GPGME_Message } from "./Message";
*/
import { permittedOperations } from './permittedOperations'
import { GPGMEJS_Error } from "./Errors"
import { GPGME_Message } from "./Message";
/**
* A Connection handles the nativeMessaging interaction.
@ -60,12 +59,11 @@ 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;
@ -75,6 +73,7 @@ export class Connection{
}
);
}
}
/**
* Sends a message and resolves with the answer.
@ -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;
}
/**

View File

@ -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 (code === 'TODO'){
alert('TODO_Error!');
}
if (!errors.hasOwnProperty(code)){
throw('GENERIC_ERROR');
if (errors.hasOwnProperty(code)){
code = 'GENERIC_ERROR';
}
let msg = code;
if (errors[code].msg !== undefined){
msg = msg + ': ' + errors[code].msg;
}
if (details){
msg = msg + ' ' + details;
}
if (config.debug === 'console'){
console.log(msg);
} else if (config.debug === 'alert'){
alert(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');
if (error.type === 'error'){
return {code: 'code',
msg: errors[code].msg
};
}
if (error.type === 'warning'){
console.log(code + ': ' + error[code].msg);
}
return undefined;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
* 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<Key>| GPGME_Key | Array<GPGME_Key>|String|Array<String>} input
* @param {Boolean} nocheck if set, an empty result is acceptable
* @returns {Array<String>} 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;
}

View File

@ -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)){

View File

@ -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');
}
}

View File

@ -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');
}
}

View File

@ -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);

View File

@ -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);