diff options
Diffstat (limited to 'lang/js/src/Connection.js')
-rw-r--r-- | lang/js/src/Connection.js | 208 |
1 files changed, 156 insertions, 52 deletions
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js index e8fea542..784929e9 100644 --- a/lang/js/src/Connection.js +++ b/lang/js/src/Connection.js @@ -1,76 +1,180 @@ +import { GPGME_Message } from "./Message"; + +/* 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+ + */ + /** * A connection port will be opened for each communication between gpgmejs and * gnupg. It should be alive as long as there are additional messages to be * expected. */ +import { permittedOperations } from './permittedOperations' -export function Connection(){ - if (!this.connection){ - this.connection = connect(); - this._msg = { - 'always-trust': true, - // 'no-encrypt-to': false, - // 'no-compress': true, - // 'throw-keyids': false, - // 'wrap': false, - 'armor': true, - 'base64': false - }; - }; +export class Connection{ - this.disconnect = function () { - if (this.connection){ - this.connection.disconnect(); + /** + * Opens and closes a port. Thus, it is made sure that the connection can + * be used. + * THIS BEHAVIOUR MAY CHANGE! + * discussion is to keep a port alive as long as the context stays the same + * + * TODO returns nothing, but triggers exceptions if not successfull + */ + constructor(){ + this._connection = chrome.runtime.connectNative('gpgmejson'); + if (!this._connection){ + if (chrome.runtime.lastError){ + throw('NO_CONNECT_RLE'); + } else { + throw('NO_CONNECT'); + } } - }; + this._flags = {}; // TODO general config + } /** - * Sends a message and resolves with the answer. - * @param {*} operation The interaction requested from gpgme - * @param {*} message A json-capable object to pass the operation details. - * TODO: _msg should contain configurable parameters + * Immediately closes the open port */ - this.post = function(operation, message){ - let timeout = 5000; - let me = this; - if (!message || !operation){ - return Promise.reject('no message'); // TBD + disconnect() { + if (this._connection){ + this._connection.disconnect(); } + } - let keys = Object.keys(message); - for (let i=0; i < keys.length; i++){ - let property = keys[i]; - me._msg[property] = message[property]; + /** + * Sends a message and resolves with the answer. + * @param {GPGME_Message} message + * @returns {Promise<Object>} the gnupg answer, or rejection with error + * information + * TODO: better/more consistent error information + */ + post(message){ + if (!message || !message instanceof GPGME_Message){ + return Promise.reject('ERR_NO_MSG'); } - me._msg['op'] = operation; - // TODO fancier checks if what we want is consistent with submitted content + // let timeout = 5000; //TODO config + let me = this; return new Promise(function(resolve, reject){ - me.connection.onMessage.addListener(function(msg) { + let answer = new Answer(message.op); + let listener = function(msg) { if (!msg){ - reject('empty answer.'); - } - if (msg.type === "error"){ + me._connection.onMessage.removeListener(listener) + reject('EMPTY_ANSWER'); + } else if (msg.type === "error"){ + me._connection.onMessage.removeListener(listener) reject(msg.msg); + } else { + answer.add(msg); + if (msg.more === true){ + me._connection.postMessage({'op': 'getmore'}); + } else { + me._connection.onMessage.removeListener(listener) + resolve(answer.message); + } } - resolve(msg); - }); + }; - me.connection.postMessage(me._msg); - setTimeout( - function(){ - me.disconnect(); - reject('Timeout'); - }, timeout); + me._connection.onMessage.addListener(listener); + me._connection.postMessage(message); + //TBD: needs to be aware if there is a pinentry pending + // setTimeout( + // function(){ + // me.disconnect(); + // reject('TIMEOUT'); + // }, timeout); }); - }; + } }; +/** + * A class for answer objects, checking and processing the return messages of + * the nativeMessaging communication + * @param {String} operation The operation, to look up validity of return keys + */ +class Answer{ -function connect(){ - let connection = chrome.runtime.connectNative('gpgmejson'); - if (!connection){ - let msg = chrome.runtime.lastError || 'no message'; //TBD - throw(msg); + constructor(operation){ + this.operation = operation; } - return connection; -}; + + /** + * + * @param {Object} msg The message as received with nativeMessaging + * TODO: "error" and "more" handling are not in here, but in post() + */ + add(msg){ + if (this._response === undefined){ + this._response = {}; + } + let messageKeys = Object.keys(msg); + let poa = permittedOperations[this.operation].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){ + console.log( 'unexpected answer type: ' + msg.type); + throw('UNEXPECTED_TYPE'); + + } + break; + case 'more': + break; + default: + //data should be concatenated + if (poa.data.indexOf(key) >= 0){ + if (!this._response.hasOwnProperty(key)){ + this._response[key] = ''; + } + this._response[key] = this._response[key].concat(msg[key]); + } + //params should not change through the message + else if (poa.params.indexOf(key) >= 0){ + if (!this._response.hasOwnProperty(key)){ + this._response[key] = msg[key]; + } + else if (this._response[key] !== msg[key]){ + throw('UNEXPECTED_TYPE'); + } + } + //infos may be json objects etc. Not yet defined. + // Pushing them into arrays for now + else if (poa.infos.indexOf(key) >= 0){ + if (!this._response.hasOwnProperty(key)){ + this._response[key] = []; + } + this._response.push(msg[key]); + } + else { + console.log('unexpected answer parameter: ' + key); + throw('UNEXPECTED_PARAM'); + } + break; + } + } + } + + /** + * Returns the assembled message. TODO: does not care yet if completed. + */ + get message(){ + return this._response; + } +} |