js: change in initialization ancd connection handling

--

* The Connection will now be started before an object is created, to
  better account for failures.
* index.js: now exposes an init(), which returns a Promise of
  configurable <GpgME | gpgmeGpgME_openPGPCompatibility> with an
  established connection.
* TODO: There is currently no way to recover from a "connection lost"
* Connection.js offers Connection.isConnected, which toggles on port
  closing.
This commit is contained in:
Maximilian Krambach 2018-04-24 18:44:30 +02:00
parent 727340b295
commit 461dd0c8b4
6 changed files with 159 additions and 116 deletions

View File

@ -35,6 +35,18 @@ export class Connection{
constructor(){
this.connect();
let me = this;
}
/**
* (Simple) Connection check.
* @returns {Boolean} true if the onDisconnect event has not been fired.
* Please note that the event listener of the port takes some time
* (5 ms seems enough) to react after the port is created. Then this will
* return undefined
*/
get isConnected(){
return this._isConnected;
}
/**
@ -48,28 +60,20 @@ export class Connection{
/**
* Opens a nativeMessaging port.
* returns nothing, but triggers errors if not successfull:
* NO_CONNECT: connection not successfull, chrome.runtime.lastError may be
* available
* ALREADY_CONNECTED: There is already a connection present.
* TODO: Error handling ALREADY_CONNECTED
*/
connect(){
if (this._connection){
if (this._isConnected === true){
return new GPGMEJS_Error('ALREADY_CONNECTED');
}
this._isConnected = true;
this._connection = chrome.runtime.connectNative('gpgmejson');
if (!this._connection){
return new GPGMEJS_Error('NO_CONNECT');
let me = this;
this._connection.onDisconnect.addListener(
function(){
me._isConnected = false;
}
}
/**
* checks if the connection is established
* TODO: some kind of ping to see if the other side responds as expected?
* @returns {Boolean}
*/
get connected(){
return this._connection ? true: false;
);
}
/**
@ -79,6 +83,9 @@ export class Connection{
* information.
*/
post(message){
if (!this.isConnected){
return Promise.reject(new GPGMEJS_Error('NO_CONNECT'));
}
if (!message || !message instanceof GPGME_Message){
return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
}

View File

@ -19,28 +19,28 @@
*/
import {GPGME_Message} from './Message'
import {Connection} from './Connection'
import {GPGME_Key} from './Key'
import { isFingerprint, isLongId } from './Helpers';
export class GPGME_Keyring {
constructor(){
this.reconnect();
constructor(connection){
this.connection = connection;
}
/**
* (Re)-establishes the connection
* TODO TEMP: should we better use the connection of our parent,
* which we do not control?
*/
reconnect(){
if (!this._connection || ! this._connection instanceof Connection){
this._connection = new Connection;
} else {
this._connection.disconnect();
this._connection.connect();
set connection(connection){
if (!this._connection && connection instanceof Connection){
this._connection = connection;
}
}
get connection(){
if (this._connection instanceof Connection){
if (this._connection.isConnected){
return this._connection;
}
return undefined; //TODO: connection was lost!
}
return undefined; //TODO: no connection there
}
/**
* @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds
@ -57,7 +57,7 @@ export class GPGME_Keyring {
msg.setParameter('with-secret', true);
}
this._connection.post(msg).then(function(result){
this.connection.post(msg).then(function(result){
let fpr_list = [];
let resultset = [];
if (!Array.isArray(result.keys)){

View File

@ -22,52 +22,44 @@ import {Connection} from "./Connection"
import {GPGME_Message} from './Message'
import {toKeyIdArray} from "./Helpers"
import {GPGMEJS_Error as Error, GPGMEJS_Error} from "./Errors"
import { GPGME_Keyring } from "./Keyring";
export class GpgME {
/**
* initializes GpgME by opening a nativeMessaging port
* TODO: add configuration
*/
constructor(configuration = {
null_expire_is_never: false
}){
this._connection = new Connection;
constructor(connection){
this.connection = connection;
}
/**
* refreshes the nativeApp connection
*/
reconnect(){
if (!this._connection || ! this._connection instanceof Connection){
this._connection = new Connection;
} else {
this._connection.disconnect();
this._connection.connect();
set connection(connection){
if (this._connection instanceof Connection){
//TODO Warning: Connection already established
}
if (connection instanceof Connection){
this._connection = connection;
}
}
/**
* inmediately tries to destroy the nativeMessaging connection.
* TODO: may not be included in final API, as it is redundant.
* For now, it just serves paranoia
*/
disconnect(){
if (this._connection){
this._connection.disconnect();
this._connection = null;
get connection(){
if (this._connection instanceof Connection){
if (this._connection.isConnected){
return this._connection;
}
return undefined; //TODO: connection was lost!
}
return undefined; //TODO: no connection there
}
set Keyring(keyring){
if (ring && ring instanceof GPGME_Keyring){
this.Keyring = ring;
}
}
/**
* tests the nativeApp connection
*/
get connected(){
if (!this._connection || ! this._connection instanceof Connection){
return false;
get Keyring(){
}
return this._connection.connected;
}
/**
* @param {String|Uint8Array} data text/data to be encrypted as String/Uint8Array
@ -89,7 +81,7 @@ export class GpgME {
if (wildcard === true){msg.setParameter('throw-keyids', true);
};
return (this._connection.post(msg));
return (this.connection.post(msg));
}
/**
@ -109,7 +101,7 @@ export class GpgME {
}
let msg = new GPGME_Message('decrypt');
putData(msg, data);
return this._connection.post(msg);
return this.connection.post(msg);
}
@ -128,7 +120,7 @@ export class GpgME {
if (no_confirm === true){ //TODO: Do we want this hidden deep in the code?
msg.setParameter('delete_force', true); //TBD
}
this._connection.post(msg).then(function(success){
this.connection.post(msg).then(function(success){
//TODO: it seems that there is always errors coming back:
}, function(error){
switch (error.msg){
@ -143,7 +135,6 @@ export class GpgME {
}
});
}
}
/**

View File

@ -30,13 +30,29 @@
import { isFingerprint } from "./Helpers"
import { GPGMEJS_Error } from './Errors'
export class GpgME_openPGPCompatibility {
constructor(){
this._gpgme = new GpgME({
null_expire_is_never: false
});
this.Keyring = this.initKeyring();
constructor(connection){
this.initGpgME(connection);
}
get Keyring(){
if (this._keyring){
return this._keyring;
}
return undefined;
}
initGpgME(connection){
this._GpgME = new GpgME(connection);
this._Keyring = new GPGME_Keyring_openPGPCompatibility(connection);
}
get GpgME(){
if (this._GpGME){
return this._GpGME;
}
}
/**
@ -128,9 +144,6 @@ export class GpgME_openPGPCompatibility {
// mime: A Boolean indicating whether the data is a MIME object.
// info: An optional object with extra information.
}
initKeyring(){
return new GPGME_Keyring_openPGPCompatibility;
}
}
/**
@ -138,8 +151,8 @@ export class GpgME_openPGPCompatibility {
* It may still be changed/expanded/merged with GPGME_Keyring
*/
class GPGME_Keyring_openPGPCompatibility {
constructor(){
this._gpgme_keyring = new GPGME_Keyring;
constructor(connection){
this._gpgme_keyring = new GPGME_Keyring(connection);
}
/**

View File

@ -18,6 +18,40 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
import { GpgME as gpgmejs } from "./gpgmejs";
// import { GpgME_openPGPCompatibility as gpgmejs } from "./gpgmejs_openpgpjs";
export default gpgmejs;
import { GpgME } from "./gpgmejs";
import { GpgME_openPGPCompatibility } from "./gpgmejs_openpgpjs";
import { Connection } from "./Connection";
/**
* Initializes a nativeMessaging Connection and returns a GPGMEjs object
* @param {*} conf Configuration. TBD
*/
function init( config = {
api_style: 'gpgme', // | gpgme_openpgpjs
null_expire_is_never: true // Boolean
}){
return new Promise(function(resolve, reject){
let connection = new Connection;
// TODO: Delayed reaction is ugly. We need to listen to the port's
// event listener in isConnected, but this takes some time (<5ms) to
// disconnect if there is no successfull connection.
let delayedreaction = function(){
if (connection.isConnected === true){
let gpgme = null;
if (config.api_style && config.api_style === 'gpgme_openpgpjs'){
resolve(
new GpgME_openPGPCompatibility(connection));
} else {
resolve(new GpgME(connection));
}
} else {
reject('NO_CONNECT');
}
};
setTimeout(delayedreaction, 5);
});
};
export default {
init: init
}

View File

@ -19,11 +19,13 @@
*
*/
function encryptbuttonclicked(event){
document.addEventListener('DOMContentLoaded', function() {
Gpgmejs.init().then(function(gpgmejs){
document.getElementById("buttonencrypt").addEventListener("click",
function(){
let data = document.getElementById('cleartext').value;
let keyId = document.getElementById('pubkey').value;
let communication = new Gpgmejs;
let enc = communication.encrypt(data, keyId).then(
gpgmejs.encrypt(data, keyId).then(
function(answer){
console.log(answer);
if (answer.data){
@ -33,12 +35,12 @@ function encryptbuttonclicked(event){
}, function(errormsg){
alert('Error: '+ errormsg);
});
};
});
function decryptbuttonclicked(event){
document.getElementById("buttondecrypt").addEventListener("click",
function(){
let data = document.getElementById("ciphertext").value;
let communication = new Gpgmejs;
let enc = communication.decrypt(data).then(
gpgmejs.decrypt(data).then(
function(answer){
console.log(answer);
if (answer.data){
@ -47,11 +49,7 @@ function decryptbuttonclicked(event){
}, function(errormsg){
alert('Error: '+ errormsg);
});
};
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("buttonencrypt").addEventListener("click",
encryptbuttonclicked);
document.getElementById("buttondecrypt").addEventListener("click",
decryptbuttonclicked);
});
},
function(error){console.log(error)});
});