js: revert changes to class read/write restriction
authorMaximilian Krambach <maximilian.krambach@intevation.de>
Mon, 20 Aug 2018 10:12:43 +0000 (12:12 +0200)
committerMaximilian Krambach <maximilian.krambach@intevation.de>
Mon, 20 Aug 2018 10:12:43 +0000 (12:12 +0200)
--

* undoes 94ee0988d4eaac27785de6efb7c19ca9976e1e9c and
  e16a87e83910ebb6bfdc4148369165f121f0997e.
  I do not fully understand why my approach was bad, but I am not in
  a position to argue. This revert was requested to me after a review,
  and I'm doing it in the assumption that more experienced people know
  better than me.
* unittests: Also changed some outdated tests that stopped working
  since 754e799d35fd62d7a979452f44342934659908c7 (as GPGME_Key is not
  exported, one cannot check for instanceof in the tests anymore)

lang/js/src/Connection.js
lang/js/src/Errors.js
lang/js/src/Key.js
lang/js/src/Keyring.js
lang/js/src/Message.js
lang/js/src/Signature.js
lang/js/src/gpgmejs.js
lang/js/src/index.js
lang/js/unittests.js

index 8d381f1..a60fd21 100644 (file)
@@ -39,144 +39,146 @@ import { decode } from './Helpers';
 export class Connection{
 
     constructor(){
-        let _connection = chrome.runtime.connectNative('gpgmejson');
-
+        this._connection = chrome.runtime.connectNative('gpgmejson');
+    }
 
-        /**
-         * Immediately closes an open port.
-         */
-        this.disconnect = function () {
-            if (_connection){
-                _connection.disconnect();
-                _connection = null;
-            }
-        };
+    /**
+     * Immediately closes an open port.
+     */
+    disconnect() {
+        if (this._connection){
+            this._connection.disconnect();
+            this._connection = null;
+        }
+    }
 
 
-        /**
-        * @typedef {Object} backEndDetails
-        * @property {String} gpgme Version number of gpgme
-        * @property {Array<Object>} info Further information about the backend
-        * and the used applications (Example:
-        * {
-        *          "protocol":     "OpenPGP",
-        *          "fname":        "/usr/bin/gpg",
-        *          "version":      "2.2.6",
-        *          "req_version":  "1.4.0",
-        *          "homedir":      "default"
-        * }
-        */
+    /**
+    * @typedef {Object} backEndDetails
+    * @property {String} gpgme Version number of gpgme
+    * @property {Array<Object>} info Further information about the backend
+    * and the used applications (Example:
+    * {
+    *          "protocol":     "OpenPGP",
+    *          "fname":        "/usr/bin/gpg",
+    *          "version":      "2.2.6",
+    *          "req_version":  "1.4.0",
+    *          "homedir":      "default"
+    * }
+    */
 
-        /**
-         * Retrieves the information about the backend.
-         * @param {Boolean} details (optional) If set to false, the promise will
-         *  just return if a connection was successful.
-         * @returns {Promise<backEndDetails>|Promise<Boolean>} Details from the
-         * backend
-         * @async
-         */
-        this.checkConnection = function(details = true){
-            const msg = createMessage('version');
-            if (details === true) {
-                return this.post(msg);
-            } else {
-                let me = this;
-                return new Promise(function(resolve) {
-                    Promise.race([
-                        me.post(msg),
-                        new Promise(function(resolve, reject){
-                            setTimeout(function(){
-                                reject(gpgme_error('CONN_TIMEOUT'));
-                            }, 500);
-                        })
-                    ]).then(function(){ // success
-                        resolve(true);
-                    }, function(){ // failure
-                        resolve(false);
-                    });
+    /**
+     * Retrieves the information about the backend.
+     * @param {Boolean} details (optional) If set to false, the promise will
+     *  just return if a connection was successful.
+     * @returns {Promise<backEndDetails>|Promise<Boolean>} Details from the
+     * backend
+     * @async
+     */
+    checkConnection (details = true){
+        const msg = createMessage('version');
+        if (details === true) {
+            return this.post(msg);
+        } else {
+            let me = this;
+            return new Promise(function(resolve) {
+                Promise.race([
+                    me.post(msg),
+                    new Promise(function(resolve, reject){
+                        setTimeout(function(){
+                            reject(gpgme_error('CONN_TIMEOUT'));
+                        }, 500);
+                    })
+                ]).then(function(){ // success
+                    resolve(true);
+                }, function(){ // failure
+                    resolve(false);
                 });
-            }
-        };
+            });
+        }
+    }
 
-        /**
-         * Sends a {@link GPGME_Message} via tghe nativeMessaging port. It
-         * resolves with the completed answer after all parts have been
-         * received and reassembled, or rejects with an {@link GPGME_Error}.
-         *
-         * @param {GPGME_Message} message
-         * @returns {Promise<Object>} The collected answer
-         * @async
-         */
-        this.post = function (message){
-            if (!message || !(message instanceof GPGME_Message)){
-                this.disconnect();
-                return Promise.reject(gpgme_error(
-                    'PARAM_WRONG', 'Connection.post'));
-            }
-            if (message.isComplete() !== true){
-                this.disconnect();
-                return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
-            }
-            let chunksize = message.chunksize;
-            return new Promise(function(resolve, reject){
-                let answer = Object.freeze(new Answer(message));
-                let listener = function(msg) {
-                    if (!msg){
-                        _connection.onMessage.removeListener(listener);
-                        _connection.disconnect();
-                        reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
+    /**
+     * Sends a {@link GPGME_Message} via tghe nativeMessaging port. It
+     * resolves with the completed answer after all parts have been
+     * received and reassembled, or rejects with an {@link GPGME_Error}.
+     *
+     * @param {GPGME_Message} message
+     * @returns {Promise<Object>} The collected answer
+     * @async
+     */
+    post(message){
+        if (!message || !(message instanceof GPGME_Message)){
+            this.disconnect();
+            return Promise.reject(gpgme_error(
+                'PARAM_WRONG', 'Connection.post'));
+        }
+        if (message.isComplete() !== true){
+            this.disconnect();
+            return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+        }
+        let chunksize = message.chunksize;
+        const me = this;
+        return new Promise(function(resolve, reject){
+            let answer = new Answer(message);
+            let listener = function(msg) {
+                if (!msg){
+                    me._connection.onMessage.removeListener(listener);
+                    me._connection.disconnect();
+                    reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
+                } else {
+                    let answer_result = answer.collect(msg);
+                    if (answer_result !== true){
+                        me._connection.onMessage.removeListener(listener);
+                        me._connection.disconnect();
+                        reject(answer_result);
                     } else {
-                        let answer_result = answer.collect(msg);
-                        if (answer_result !== true){
-                            _connection.onMessage.removeListener(listener);
-                            _connection.disconnect();
-                            reject(answer_result);
+                        if (msg.more === true){
+                            me._connection.postMessage({
+                                'op': 'getmore',
+                                'chunksize': chunksize
+                            });
                         } else {
-                            if (msg.more === true){
-                                _connection.postMessage({
-                                    'op': 'getmore',
-                                    'chunksize': chunksize
-                                });
+                            me._connection.onMessage.removeListener(listener);
+                            me._connection.disconnect();
+                            const message = answer.getMessage();
+                            if (message instanceof Error){
+                                reject(message);
                             } else {
-                                _connection.onMessage.removeListener(listener);
-                                _connection.disconnect();
-                                const message = answer.getMessage();
-                                if (message instanceof Error){
-                                    reject(message);
-                                } else {
-                                    resolve(message);
-                                }
+                                resolve(message);
                             }
                         }
                     }
-                };
-                _connection.onMessage.addListener(listener);
-                if (permittedOperations[message.operation].pinentry){
-                    return _connection.postMessage(message.message);
-                } else {
-                    return Promise.race([
-                        _connection.postMessage(message.message),
-                        function(resolve, reject){
-                            setTimeout(function(){
-                                _connection.disconnect();
-                                reject(gpgme_error('CONN_TIMEOUT'));
-                            }, 5000);
-                        }]).then(function(result){
-                        return result;
-                    }, function(reject){
-                        if(!(reject instanceof Error)) {
-                            _connection.disconnect();
-                            return gpgme_error('GNUPG_ERROR', reject);
-                        } else {
-                            return reject;
-                        }
-                    });
                 }
-            });
-        };
+            };
+            me._connection.onMessage.addListener(listener);
+            if (permittedOperations[message.operation].pinentry){
+                return me._connection.postMessage(message.message);
+            } else {
+                return Promise.race([
+                    me._connection.postMessage(message.message),
+                    function(resolve, reject){
+                        setTimeout(function(){
+                            me._connection.disconnect();
+                            reject(gpgme_error('CONN_TIMEOUT'));
+                        }, 5000);
+                    }
+                ]).then(function(result){
+                    return result;
+                }, function(reject){
+                    if(!(reject instanceof Error)) {
+                        me._connection.disconnect();
+                        return gpgme_error('GNUPG_ERROR', reject);
+                    } else {
+                        return reject;
+                    }
+                });
+            }
+        });
     }
 }
 
+
 /**
  * A class for answer objects, checking and processing the return messages of
  * the nativeMessaging communication.
@@ -188,95 +190,94 @@ class Answer{
      * @param {GPGME_Message} message
      */
     constructor(message){
-        const operation = message.operation;
-        const expected = message.getExpect();
-        let response_b64 = null;
+        this._operation = message.operation;
+        this._expected = message.expected;
+        this._response_b64 = null;
+    }
 
-        this.getOperation = function(){
-            return operation;
-        };
+    get operation (){
+        return this._operation;
+    }
 
-        this.getExpect = function(){
-            return expected;
-        };
+    get expected (){
+        return this._expected;
+    }
 
-        /**
-         * Adds incoming base64 encoded data to the existing response
-         * @param {*} msg base64 encoded data.
-         * @returns {Boolean}
-         *
-         * @private
-         */
-        this.collect = function (msg){
-            if (typeof(msg) !== 'object' || !msg.hasOwnProperty('response')) {
-                return gpgme_error('CONN_UNEXPECTED_ANSWER');
-            }
-            if (response_b64 === null){
-                response_b64 = msg.response;
-                return true;
-            } else {
-                response_b64 += msg.response;
-                return true;
-            }
-        };
-        /**
-         * Returns the base64 encoded answer data with the content verified
-         * against {@link permittedOperations}.
-         */
-        this.getMessage = function (){
-            if (response_b64 === undefined){
-                return gpgme_error('CONN_UNEXPECTED_ANSWER');
-            }
-            let _decodedResponse = JSON.parse(atob(response_b64));
-            let _response = {};
-            let messageKeys = Object.keys(_decodedResponse);
-            let poa = permittedOperations[this.getOperation()].answer;
-            if (messageKeys.length === 0){
-                return gpgme_error('CONN_UNEXPECTED_ANSWER');
-            }
-            for (let i= 0; i < messageKeys.length; i++){
-                let key = messageKeys[i];
-                switch (key) {
-                case 'type':
-                    if (_decodedResponse.type === 'error'){
-                        return (gpgme_error('GNUPG_ERROR',
-                            decode(_decodedResponse.msg)));
-                    } else if (poa.type.indexOf(_decodedResponse.type) < 0){
-                        return gpgme_error('CONN_UNEXPECTED_ANSWER');
-                    }
-                    break;
-                case 'base64':
-                    break;
-                case 'msg':
-                    if (_decodedResponse.type === 'error'){
-                        return (gpgme_error('GNUPG_ERROR',
-                            _decodedResponse.msg));
-                    }
-                    break;
-                default:
-                    if (!poa.data.hasOwnProperty(key)){
-                        return gpgme_error('CONN_UNEXPECTED_ANSWER');
-                    }
-                    if( typeof(_decodedResponse[key]) !== poa.data[key] ){
-                        return gpgme_error('CONN_UNEXPECTED_ANSWER');
-                    }
-                    if (_decodedResponse.base64 === true
-                        && poa.data[key] === 'string'
-                        && this.getExpect() !== 'base64'
-                    ){
-                        _response[key] = decodeURIComponent(
-                            atob(_decodedResponse[key]).split('').map(
-                                function(c) {
-                                    return '%' +
-                                ('00' + c.charCodeAt(0).toString(16)).slice(-2);
-                                }).join(''));
-                    } else {
-                        _response[key] = decode(_decodedResponse[key]);
-                    }
-                    break;
+    /**
+     * Adds incoming base64 encoded data to the existing response
+     * @param {*} msg base64 encoded data.
+     * @returns {Boolean}
+     *
+     * @private
+     */
+    collect (msg){
+        if (typeof(msg) !== 'object' || !msg.hasOwnProperty('response')) {
+            return gpgme_error('CONN_UNEXPECTED_ANSWER');
+        }
+        if (!this._response_b64){
+            this._response_b64 = msg.response;
+            return true;
+        } else {
+            this._response_b64 += msg.response;
+            return true;
+        }
+    }
+    /**
+     * Returns the base64 encoded answer data with the content verified
+     * against {@link permittedOperations}.
+     */
+    getMessage(){
+        if (this._response_b64 === null){
+            return gpgme_error('CONN_UNEXPECTED_ANSWER');
+        }
+        let _decodedResponse = JSON.parse(atob(this._response_b64));
+        let _response = {};
+        let messageKeys = Object.keys(_decodedResponse);
+        let poa = permittedOperations[this.operation].answer;
+        if (messageKeys.length === 0){
+            return gpgme_error('CONN_UNEXPECTED_ANSWER');
+        }
+        for (let i= 0; i < messageKeys.length; i++){
+            let key = messageKeys[i];
+            switch (key) {
+            case 'type':
+                if (_decodedResponse.type === 'error'){
+                    return (gpgme_error('GNUPG_ERROR',
+                        decode(_decodedResponse.msg)));
+                } else if (poa.type.indexOf(_decodedResponse.type) < 0){
+                    return gpgme_error('CONN_UNEXPECTED_ANSWER');
+                }
+                break;
+            case 'base64':
+                break;
+            case 'msg':
+                if (_decodedResponse.type === 'error'){
+                    return (gpgme_error('GNUPG_ERROR', _decodedResponse.msg));
+                }
+                break;
+            default:
+                if (!poa.data.hasOwnProperty(key)){
+                    return gpgme_error('CONN_UNEXPECTED_ANSWER');
+                }
+                if( typeof(_decodedResponse[key]) !== poa.data[key] ){
+                    return gpgme_error('CONN_UNEXPECTED_ANSWER');
+                }
+                if (_decodedResponse.base64 === true
+                    && poa.data[key] === 'string'
+                    && this.expected !== 'base64'
+                ){
+                    _response[key] = decodeURIComponent(
+                        atob(_decodedResponse[key]).split('').map(
+                            function(c) {
+                                return '%' +
+                            ('00' + c.charCodeAt(0).toString(16)).slice(-2);
+                            }).join(''));
+                } else {
+                    _response[key] = decode(_decodedResponse[key]);
                 }
+                break;
             }
-            return _response;
-        };
+        }
+        return _response;
     }
 }
index b22eca7..2a35bc5 100644 (file)
@@ -123,7 +123,7 @@ const err_list = {
 export function gpgme_error(code = 'GENERIC_ERROR', info){
     if (err_list.hasOwnProperty(code)){
         if (err_list[code].type === 'error'){
-            return Object.freeze(new GPGME_Error(code));
+            return new GPGME_Error(code);
         }
         if (err_list[code].type === 'warning'){
             // eslint-disable-next-line no-console
@@ -131,10 +131,10 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){
         }
         return null;
     } else if (code === 'GNUPG_ERROR'){
-        return Object.freeze(new GPGME_Error(code, info));
+        return new GPGME_Error(code, info);
     }
     else {
-        return Object.freeze(new GPGME_Error('GENERIC_ERROR'));
+        return new GPGME_Error('GENERIC_ERROR');
     }
 }
 
@@ -148,6 +148,7 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){
  */
 class GPGME_Error extends Error{
     constructor(code = 'GENERIC_ERROR', msg=''){
+
         if (code === 'GNUPG_ERROR' && typeof(msg) === 'string'){
             super(msg);
         } else if (err_list.hasOwnProperty(code)){
@@ -159,12 +160,10 @@ class GPGME_Error extends Error{
         } else {
             super(err_list['GENERIC_ERROR'].msg);
         }
-        this.getCode = function(){
-            return code;
-        };
+        this._code = code;
     }
 
     get code(){
-        return this.getCode();
+        return this._code;
     }
 }
\ No newline at end of file
index ea6fd88..37ec7f9 100644 (file)
@@ -40,12 +40,12 @@ export function createKey(fingerprint, async = false, data){
         return gpgme_error('PARAM_WRONG');
     }
     if (data !== undefined){
-        data = validateKeyData(data);
+        data = validateKeyData(fingerprint, data);
     }
     if (data instanceof Error){
         return gpgme_error('KEY_INVALID');
     } else {
-        return Object.freeze(new GPGME_Key(fingerprint, async, data));
+        return new GPGME_Key(fingerprint, async, data);
     }
 }
 
@@ -65,184 +65,181 @@ class GPGME_Key {
         /**
          * @property {Boolean} If true, most answers will be asynchronous
          */
-        this.isAsync = async;
+        this._async = async;
 
-        let _data = {fingerprint: fingerprint.toUpperCase()};
+        this._data = {fingerprint: fingerprint.toUpperCase()};
         if (data !== undefined
-            && data.fingerprint.toUpperCase() === _data.fingerprint
+            && data.fingerprint.toUpperCase() === this._data.fingerprint
         ) {
-            _data = data;
+            this._data = data;
         }
-        this.getFingerprint = function(){
-            if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){
-                return gpgme_error('KEY_INVALID');
-            }
-            return _data.fingerprint;
-        };
+    }
 
-        /**
-         * Query any property of the Key listed in {@link validKeyProperties}
-         * @param {String} property property to be retreived
-         * @returns {Boolean| String | Date | Array | Object |GPGME_Error}
-         * the value of the property. If the Key is set to Async, the value
-         * will be fetched from gnupg and resolved as a Promise. If Key is not
-         * async, the armored property is not available (it can still be
-         * retrieved asynchronously by {@link Key.getArmor})
-         */
-        this.get = function(property) {
-            if (this.isAsync === true) {
-                switch (property){
-                case 'armored':
-                    return this.getArmor();
-                case 'hasSecret':
-                    return this.getGnupgSecretState();
-                default:
-                    return getGnupgState(property);
-                }
+    /**
+     * Query any property of the Key listed in {@link validKeyProperties}
+     * @param {String} property property to be retreived
+     * @returns {Boolean| String | Date | Array | Object |GPGME_Error}
+     * the value of the property. If the Key is set to Async, the value
+     * will be fetched from gnupg and resolved as a Promise. If Key is not
+     * async, the armored property is not available (it can still be
+     * retrieved asynchronously by {@link Key.getArmor})
+     */
+    get(property) {
+        if (this._async === true) {
+            switch (property){
+            case 'armored':
+                return this.getArmor();
+            case 'hasSecret':
+                return this.getGnupgSecretState();
+            default:
+                return getGnupgState(this.fingerprint, property);
+            }
+        } else {
+            if (property === 'armored') {
+                return gpgme_error('KEY_ASYNC_ONLY');
+            }
+            if (!validKeyProperties.hasOwnProperty(property)){
+                return gpgme_error('PARAM_WRONG');
             } else {
-                if (property === 'armored') {
-                    return gpgme_error('KEY_ASYNC_ONLY');
-                }
-                if (!validKeyProperties.hasOwnProperty(property)){
-                    return gpgme_error('PARAM_WRONG');
-                } else {
-                    return (_data[property]);
-                }
+                return (this._data[property]);
             }
-        };
+        }
+    }
 
-        /**
-         * Reloads the Key information from gnupg. This is only useful if you
-         * use the GPGME_Keys cached. Note that this is a performance hungry
-         * operation. If you desire more than a few refreshs, it may be
-         * advisable to run {@link Keyring.getKeys} instead.
-         * @returns {Promise<GPGME_Key|GPGME_Error>}
-         * @async
-         */
-        this.refreshKey = function() {
-            let me = this;
-            return new Promise(function(resolve, reject) {
-                if (!_data.fingerprint){
-                    reject(gpgme_error('KEY_INVALID'));
-                }
-                let msg = createMessage('keylist');
-                msg.setParameter('sigs', true);
-                msg.setParameter('keys', _data.fingerprint);
-                msg.post().then(function(result){
-                    if (result.keys.length === 1){
-                        const newdata = validateKeyData(
-                            _data.fingerprint, result.keys[0]);
-                        if (newdata instanceof Error){
-                            reject(gpgme_error('KEY_INVALID'));
-                        } else {
-                            _data = newdata;
-                            me.getGnupgSecretState().then(function(){
-                                me.getArmor().then(function(){
-                                    resolve(me);
-                                }, function(error){
-                                    reject(error);
-                                });
+    /**
+     * Reloads the Key information from gnupg. This is only useful if you
+     * use the GPGME_Keys cached. Note that this is a performance hungry
+     * operation. If you desire more than a few refreshs, it may be
+     * advisable to run {@link Keyring.getKeys} instead.
+     * @returns {Promise<GPGME_Key|GPGME_Error>}
+     * @async
+     */
+    refreshKey() {
+        let me = this;
+        return new Promise(function(resolve, reject) {
+            if (!me._data.fingerprint){
+                reject(gpgme_error('KEY_INVALID'));
+            }
+            let msg = createMessage('keylist');
+            msg.setParameter('sigs', true);
+            msg.setParameter('keys', me._data.fingerprint);
+            msg.post().then(function(result){
+                if (result.keys.length === 1){
+                    const newdata = validateKeyData(
+                        me._data.fingerprint, result.keys[0]);
+                    if (newdata instanceof Error){
+                        reject(gpgme_error('KEY_INVALID'));
+                    } else {
+                        me._data = newdata;
+                        me.getGnupgSecretState().then(function(){
+                            me.getArmor().then(function(){
+                                resolve(me);
                             }, function(error){
                                 reject(error);
                             });
-                        }
-                    } else {
-                        reject(gpgme_error('KEY_NOKEY'));
+                        }, function(error){
+                            reject(error);
+                        });
                     }
-                }, function (error) {
-                    reject(gpgme_error('GNUPG_ERROR'), error);
-                });
-            });
-        };
-
-        /**
-         * Query the armored block of the Key directly from gnupg. Please note
-         * that this will not get you any export of the secret/private parts of
-         * a Key
-         * @returns {Promise<String|GPGME_Error>}
-         * @async
-         */
-        this.getArmor = function(){
-            return new Promise(function(resolve, reject) {
-                if (!_data.fingerprint){
-                    reject(gpgme_error('KEY_INVALID'));
+                } else {
+                    reject(gpgme_error('KEY_NOKEY'));
                 }
-                let msg = createMessage('export');
-                msg.setParameter('armor', true);
-                msg.setParameter('keys', _data.fingerprint);
-                msg.post().then(function(result){
-                    resolve(result.data);
-                }, function(error){
-                    reject(error);
-                });
+            }, function (error) {
+                reject(gpgme_error('GNUPG_ERROR'), error);
             });
-        };
+        });
+    }
 
-        /**
-         * Find out if the Key is part of a Key pair including public and
-         * private key(s). If you want this information about more than a few
-         * Keys in synchronous mode, it may be advisable to run
-         * {@link Keyring.getKeys} instead, as it performs faster in bulk
-         * querying this state.
-         * @returns {Promise<Boolean|GPGME_Error>} True if a private Key is
-         * available in the gnupg Keyring.
-         * @async
-         */
-        this.getGnupgSecretState = function (){
-            return new Promise(function(resolve, reject) {
-                if (!_data.fingerprint){
-                    reject(gpgme_error('KEY_INVALID'));
-                } else {
-                    let msg = createMessage('keylist');
-                    msg.setParameter('keys', _data.fingerprint);
-                    msg.setParameter('secret', true);
-                    msg.post().then(function(result){
-                        _data.hasSecret = null;
-                        if (
-                            result.keys &&
-                            result.keys.length === 1 &&
-                            result.keys[0].secret === true
-                        ) {
-                            _data.hasSecret = true;
-                            resolve(true);
-                        } else {
-                            _data.hasSecret = false;
-                            resolve(false);
-                        }
-                    }, function(error){
-                        reject(error);
-                    });
-                }
+    /**
+     * Query the armored block of the Key directly from gnupg. Please note
+     * that this will not get you any export of the secret/private parts of
+     * a Key
+     * @returns {Promise<String|GPGME_Error>}
+     * @async
+     */
+    getArmor() {
+        const me = this;
+        return new Promise(function(resolve, reject) {
+            if (!me._data.fingerprint){
+                reject(gpgme_error('KEY_INVALID'));
+            }
+            let msg = createMessage('export');
+            msg.setParameter('armor', true);
+            msg.setParameter('keys', me._data.fingerprint);
+            msg.post().then(function(result){
+                resolve(result.data);
+            }, function(error){
+                reject(error);
             });
-        };
+        });
+    }
 
-        /**
-         * Deletes the (public) Key from the GPG Keyring. Note that a deletion
-         * of a secret key is not supported by the native backend.
-         * @returns {Promise<Boolean|GPGME_Error>} Success if key was deleted,
-         * rejects with a GPG error otherwise.
-         */
-        this.delete= function (){
-            return new Promise(function(resolve, reject){
-                if (!_data.fingerprint){
-                    reject(gpgme_error('KEY_INVALID'));
-                }
-                let msg = createMessage('delete');
-                msg.setParameter('key', _data.fingerprint);
+    /**
+     * Find out if the Key is part of a Key pair including public and
+     * private key(s). If you want this information about more than a few
+     * Keys in synchronous mode, it may be advisable to run
+     * {@link Keyring.getKeys} instead, as it performs faster in bulk
+     * querying this state.
+     * @returns {Promise<Boolean|GPGME_Error>} True if a private Key is
+     * available in the gnupg Keyring.
+     * @async
+     */
+    getGnupgSecretState (){
+        const me = this;
+        return new Promise(function(resolve, reject) {
+            if (!me._data.fingerprint){
+                reject(gpgme_error('KEY_INVALID'));
+            } else {
+                let msg = createMessage('keylist');
+                msg.setParameter('keys', me._data.fingerprint);
+                msg.setParameter('secret', true);
                 msg.post().then(function(result){
-                    resolve(result.success);
+                    me._data.hasSecret = null;
+                    if (
+                        result.keys &&
+                        result.keys.length === 1 &&
+                        result.keys[0].secret === true
+                    ) {
+                        me._data.hasSecret = true;
+                        resolve(true);
+                    } else {
+                        me._data.hasSecret = false;
+                        resolve(false);
+                    }
                 }, function(error){
                     reject(error);
                 });
+            }
+        });
+    }
+
+    /**
+     * Deletes the (public) Key from the GPG Keyring. Note that a deletion
+     * of a secret key is not supported by the native backend.
+     * @returns {Promise<Boolean|GPGME_Error>} Success if key was deleted,
+     * rejects with a GPG error otherwise.
+     */
+    delete(){
+        const me = this;
+        return new Promise(function(resolve, reject){
+            if (!me._data.fingerprint){
+                reject(gpgme_error('KEY_INVALID'));
+            }
+            let msg = createMessage('delete');
+            msg.setParameter('key', me._data.fingerprint);
+            msg.post().then(function(result){
+                resolve(result.success);
+            }, function(error){
+                reject(error);
             });
-        };
+        });
     }
 
     /**
      * @returns {String} The fingerprint defining this Key. Convenience getter
      */
     get fingerprint(){
-        return this.getFingerprint();
+        return this._data.fingerprint;
     }
 }
 
@@ -259,8 +256,9 @@ class GPGME_Subkey {
      * @private
      */
     constructor(data){
-        let _data = {};
+        this._data = {};
         let keys = Object.keys(data);
+        const me = this;
 
         /**
          * Validates a subkey property against {@link validSubKeyProperties} and
@@ -273,9 +271,9 @@ class GPGME_Subkey {
             if (validSubKeyProperties.hasOwnProperty(property)){
                 if (validSubKeyProperties[property](value) === true) {
                     if (property === 'timestamp' || property === 'expires'){
-                        _data[property] = new Date(value * 1000);
+                        me._data[property] = new Date(value * 1000);
                     } else {
-                        _data[property] = value;
+                        me._data[property] = value;
                     }
                 }
             }
@@ -283,18 +281,19 @@ class GPGME_Subkey {
         for (let i=0; i< keys.length; i++) {
             setProperty(keys[i], data[keys[i]]);
         }
+    }
 
-        /**
-         * Fetches any information about this subkey
-         * @param {String} property Information to request
-         * @returns {String | Number | Date}
-         */
-        this.get = function(property) {
-            if (_data.hasOwnProperty(property)){
-                return (_data[property]);
-            }
-        };
+    /**
+     * Fetches any information about this subkey
+     * @param {String} property Information to request
+     * @returns {String | Number | Date}
+     */
+    get(property) {
+        if (this._data.hasOwnProperty(property)){
+            return (this._data[property]);
+        }
     }
+
 }
 
 /**
@@ -310,15 +309,16 @@ class GPGME_UserId {
      * @private
      */
     constructor(data){
-        let _data = {};
+        this._data = {};
+        const me = this;
         let keys = Object.keys(data);
         const setProperty = function(property, value){
             if (validUserIdProperties.hasOwnProperty(property)){
                 if (validUserIdProperties[property](value) === true) {
                     if (property === 'last_update'){
-                        _data[property] = new Date(value*1000);
+                        me._data[property] = new Date(value*1000);
                     } else {
-                        _data[property] = value;
+                        me._data[property] = value;
                     }
                 }
             }
@@ -326,18 +326,19 @@ class GPGME_UserId {
         for (let i=0; i< keys.length; i++) {
             setProperty(keys[i], data[keys[i]]);
         }
+    }
 
-        /**
-         * Fetches information about the user
-         * @param {String} property Information to request
-         * @returns {String | Number}
-         */
-        this.get = function (property) {
-            if (_data.hasOwnProperty(property)){
-                return (_data[property]);
-            }
-        };
+    /**
+     * Fetches information about the user
+     * @param {String} property Information to request
+     * @returns {String | Number}
+     */
+    get(property) {
+        if (this._data.hasOwnProperty(property)){
+            return (this._data[property]);
+        }
     }
+
 }
 
 /**
@@ -569,10 +570,11 @@ const validKeyProperties = {
 * an error if something went wrong.
 * @private
 */
-function validateKeyData(data){
+function validateKeyData(fingerprint, data){
     const key = {};
-    if ( typeof(data) !== 'object'
-    || !data.fingerprint){
+    if (!fingerprint || typeof(data) !== 'object' || !data.fingerprint
+     || fingerprint !== data.fingerprint.toUpperCase()
+    ){
         return gpgme_error('KEY_INVALID');
     }
     let props = Object.keys(data);
@@ -588,15 +590,15 @@ function validateKeyData(data){
         case 'subkeys':
             key.subkeys = [];
             for (let i=0; i< data.subkeys.length; i++) {
-                key.subkeys.push(Object.freeze(
-                    new GPGME_Subkey(data.subkeys[i])));
+                key.subkeys.push(
+                    new GPGME_Subkey(data.subkeys[i]));
             }
             break;
         case 'userids':
             key.userids = [];
             for (let i=0; i< data.userids.length; i++) {
-                key.userids.push(Object.freeze(
-                    new GPGME_UserId(data.userids[i])));
+                key.userids.push(
+                    new GPGME_UserId(data.userids[i]));
             }
             break;
         case 'last_update':
@@ -623,19 +625,19 @@ function getGnupgState (fingerprint, property){
         } else {
             let msg = createMessage('keylist');
             msg.setParameter('keys', fingerprint);
-            msg.post().then(function(result){
-                if (!result.keys || result.keys.length !== 1){
+            msg.post().then(function(res){
+                if (!res.keys || res.keys.length !== 1){
                     reject(gpgme_error('KEY_INVALID'));
                 } else {
-                    const key = result.keys[0];
+                    const key = res.keys[0];
                     let result;
                     switch (property){
                     case 'subkeys':
                         result = [];
                         if (key.subkeys.length){
                             for (let i=0; i < key.subkeys.length; i++) {
-                                result.push(Object.freeze(
-                                    new GPGME_Subkey(key.subkeys[i])));
+                                result.push(
+                                    new GPGME_Subkey(key.subkeys[i]));
                             }
                         }
                         resolve(result);
@@ -644,8 +646,8 @@ function getGnupgState (fingerprint, property){
                         result = [];
                         if (key.userids.length){
                             for (let i=0; i< key.userids.length; i++) {
-                                result.push(Object.freeze(
-                                    new GPGME_UserId(key.userids[i])));
+                                result.push(
+                                    new GPGME_UserId(key.userids[i]));
                             }
                         }
                         resolve(result);
index d18fb64..de21736 100644 (file)
@@ -32,383 +32,384 @@ import { gpgme_error } from './Errors';
  */
 export class GPGME_Keyring {
     constructor(){
+    }
 
-        /**
-         * Queries Keys (all Keys or a subset) from gnupg.
-         *
-         * @param {String | Array<String>} pattern (optional) A pattern to
-         * search for in userIds or KeyIds.
-         * @param {Boolean} prepare_sync (optional) if set to true, most data
-         * (with the exception of armored Key blocks) will be cached for the
-         * Keys. This enables direct, synchronous use of these properties for
-         * all keys. It does not check for changes on the backend. The cached
-         * information can be updated with the {@link Key.refresh} method.
-         * @param {Boolean} search (optional) retrieve Keys from external
-         * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup)
-         * @returns {Promise<Array<GPGME_Key>>}
-         * @static
-         * @async
-         */
-        this.getKeys = function(pattern, prepare_sync=false, search=false){
-            return new Promise(function(resolve, reject) {
-                let msg = createMessage('keylist');
-                if (pattern !== undefined && pattern !== null){
-                    msg.setParameter('keys', pattern);
-                }
-                msg.setParameter('sigs', true);
-                if (search === true){
-                    msg.setParameter('locate', true);
-                }
-                msg.post().then(function(result){
-                    let resultset = [];
-                    if (result.keys.length === 0){
-                        resolve([]);
+    /**
+     * Queries Keys (all Keys or a subset) from gnupg.
+     *
+     * @param {String | Array<String>} pattern (optional) A pattern to
+     * search for in userIds or KeyIds.
+     * @param {Boolean} prepare_sync (optional) if set to true, most data
+     * (with the exception of armored Key blocks) will be cached for the
+     * Keys. This enables direct, synchronous use of these properties for
+     * all keys. It does not check for changes on the backend. The cached
+     * information can be updated with the {@link Key.refresh} method.
+     * @param {Boolean} search (optional) retrieve Keys from external
+     * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup)
+     * @returns {Promise<Array<GPGME_Key>>}
+     * @static
+     * @async
+     */
+    getKeys (pattern, prepare_sync=false, search=false){
+        return new Promise(function(resolve, reject) {
+            let msg = createMessage('keylist');
+            if (pattern !== undefined && pattern !== null){
+                msg.setParameter('keys', pattern);
+            }
+            msg.setParameter('sigs', true);
+            if (search === true){
+                msg.setParameter('locate', true);
+            }
+            msg.post().then(function(result){
+                let resultset = [];
+                if (result.keys.length === 0){
+                    resolve([]);
+                } else {
+                    let secondrequest;
+                    if (prepare_sync === true) {
+                        secondrequest = function() {
+                            let msg2 = createMessage('keylist');
+                            msg2.setParameter('keys', pattern);
+                            msg2.setParameter('secret', true);
+                            return msg2.post();
+                        };
                     } else {
-                        let secondrequest;
-                        if (prepare_sync === true) {
-                            secondrequest = function() {
-                                let msg2 = createMessage('keylist');
-                                msg2.setParameter('keys', pattern);
-                                msg2.setParameter('secret', true);
-                                return msg2.post();
-                            };
-                        } else {
-                            secondrequest = function() {
-                                return Promise.resolve(true);
-                            };
-                        }
-                        secondrequest().then(function(answer) {
-                            for (let i=0; i < result.keys.length; i++){
-                                if (prepare_sync === true){
-                                    if (answer && answer.keys) {
-                                        for (let j=0;
-                                            j < answer.keys.length; j++ ){
-                                            const a = answer.keys[j];
-                                            const b = result.keys[i];
-                                            if (
-                                                a.fingerprint === b.fingerprint
-                                            ) {
-                                                if (a.secret === true){
-                                                    b.hasSecret = true;
-                                                } else {
-                                                    b.hasSecret = false;
-                                                }
-                                                break;
+                        secondrequest = function() {
+                            return Promise.resolve(true);
+                        };
+                    }
+                    secondrequest().then(function(answer) {
+                        for (let i=0; i < result.keys.length; i++){
+                            if (prepare_sync === true){
+                                if (answer && answer.keys) {
+                                    for (let j=0;
+                                        j < answer.keys.length; j++ ){
+                                        const a = answer.keys[j];
+                                        const b = result.keys[i];
+                                        if (
+                                            a.fingerprint === b.fingerprint
+                                        ) {
+                                            if (a.secret === true){
+                                                b.hasSecret = true;
+                                            } else {
+                                                b.hasSecret = false;
                                             }
+                                            break;
                                         }
                                     }
                                 }
-                                let k = createKey(result.keys[i].fingerprint,
-                                    !prepare_sync, result.keys[i]);
-                                resultset.push(k);
                             }
-                            resolve(resultset);
-                        }, function(error){
-                            reject(error);
-                        });
-                    }
-                });
+                            let k = createKey(result.keys[i].fingerprint,
+                                !prepare_sync, result.keys[i]);
+                            resultset.push(k);
+                        }
+                        resolve(resultset);
+                    }, function(error){
+                        reject(error);
+                    });
+                }
             });
-        };
+        });
+    }
 
-        /**
-         * @typedef {Object} exportResult The result of a getKeysArmored
-         * operation.
-         * @property {String} armored The public Key(s) as armored block. Note
-         * that the result is one armored block, and not a block per key.
-         * @property {Array<String>} secret_fprs (optional) list of
-         * fingerprints for those Keys that also have a secret Key available in
-         * gnupg. The secret key will not be exported, but the fingerprint can
-         * be used in operations needing a secret key.
-         */
+    /**
+     * @typedef {Object} exportResult The result of a getKeysArmored
+     * operation.
+     * @property {String} armored The public Key(s) as armored block. Note
+     * that the result is one armored block, and not a block per key.
+     * @property {Array<String>} secret_fprs (optional) list of
+     * fingerprints for those Keys that also have a secret Key available in
+     * gnupg. The secret key will not be exported, but the fingerprint can
+     * be used in operations needing a secret key.
+     */
 
-        /**
-         * Fetches the armored public Key blocks for all Keys matching the
-         * pattern (if no pattern is given, fetches all keys known to gnupg).
-         * @param {String|Array<String>} pattern (optional) The Pattern to
-         * search for
-         * @param {Boolean} with_secret_fpr (optional) also return a list of
-         * fingerprints for the keys that have a secret key available
-         * @returns {Promise<exportResult|GPGME_Error>} Object containing the
-         * armored Key(s) and additional information.
-         * @static
-         * @async
-         */
-        this.getKeysArmored = function(pattern, with_secret_fpr) {
-            return new Promise(function(resolve, reject) {
-                let msg = createMessage('export');
-                msg.setParameter('armor', true);
-                if (with_secret_fpr === true) {
-                    msg.setParameter('with-sec-fprs', true);
-                }
-                if (pattern !== undefined && pattern !== null){
-                    msg.setParameter('keys', pattern);
+    /**
+     * Fetches the armored public Key blocks for all Keys matching the
+     * pattern (if no pattern is given, fetches all keys known to gnupg).
+     * @param {String|Array<String>} pattern (optional) The Pattern to
+     * search for
+     * @param {Boolean} with_secret_fpr (optional) also return a list of
+     * fingerprints for the keys that have a secret key available
+     * @returns {Promise<exportResult|GPGME_Error>} Object containing the
+     * armored Key(s) and additional information.
+     * @static
+     * @async
+     */
+    getKeysArmored (pattern, with_secret_fpr) {
+        return new Promise(function(resolve, reject) {
+            let msg = createMessage('export');
+            msg.setParameter('armor', true);
+            if (with_secret_fpr === true) {
+                msg.setParameter('with-sec-fprs', true);
+            }
+            if (pattern !== undefined && pattern !== null){
+                msg.setParameter('keys', pattern);
+            }
+            msg.post().then(function(answer){
+                const result = {armored: answer.data};
+                if (with_secret_fpr === true
+                    && answer.hasOwnProperty('sec-fprs')
+                ) {
+                    result.secret_fprs = answer['sec-fprs'];
                 }
-                msg.post().then(function(answer){
-                    const result = {armored: answer.data};
-                    if (with_secret_fpr === true
-                        && answer.hasOwnProperty('sec-fprs')
-                    ) {
-                        result.secret_fprs = answer['sec-fprs'];
-                    }
-                    resolve(result);
-                }, function(error){
-                    reject(error);
-                });
+                resolve(result);
+            }, function(error){
+                reject(error);
             });
-        };
+        });
+    }
 
-        /**
-         * Returns the Key used by default in gnupg.
-         * (a.k.a. 'primary Key or 'main key').
-         * It looks up the gpg configuration if set, or the first key that
-         * contains a secret key.
-         *
-         * @returns {Promise<GPGME_Key|GPGME_Error>}
-         * @async
-         * @static
-         */
-        this.getDefaultKey = function(prepare_sync = false) {
-            let me = this;
-            return new Promise(function(resolve, reject){
-                let msg = createMessage('config_opt');
-                msg.setParameter('component', 'gpg');
-                msg.setParameter('option', 'default-key');
-                msg.post().then(function(resp){
-                    if (resp.option !== undefined
-                        && resp.option.hasOwnProperty('value')
-                        && resp.option.value.length === 1
-                        && resp.option.value[0].hasOwnProperty('string')
-                        && typeof(resp.option.value[0].string) === 'string'){
-                        me.getKeys(resp.option.value[0].string, true).then(
-                            function(keys){
-                                if(keys.length === 1){
-                                    resolve(keys[0]);
-                                } else {
-                                    reject(gpgme_error('KEY_NO_DEFAULT'));
-                                }
-                            }, function(error){
-                                reject(error);
-                            });
-                    } else {
-                        let msg = createMessage('keylist');
-                        msg.setParameter('secret', true);
-                        msg.post().then(function(result){
-                            if (result.keys.length === 0){
-                                reject(gpgme_error('KEY_NO_DEFAULT'));
+    /**
+     * Returns the Key used by default in gnupg.
+     * (a.k.a. 'primary Key or 'main key').
+     * It looks up the gpg configuration if set, or the first key that
+     * contains a secret key.
+     *
+     * @returns {Promise<GPGME_Key|GPGME_Error>}
+     * @async
+     * @static
+     */
+    getDefaultKey(prepare_sync = false) {
+        let me = this;
+        return new Promise(function(resolve, reject){
+            let msg = createMessage('config_opt');
+            msg.setParameter('component', 'gpg');
+            msg.setParameter('option', 'default-key');
+            msg.post().then(function(resp){
+                if (resp.option !== undefined
+                    && resp.option.hasOwnProperty('value')
+                    && resp.option.value.length === 1
+                    && resp.option.value[0].hasOwnProperty('string')
+                    && typeof(resp.option.value[0].string) === 'string'){
+                    me.getKeys(resp.option.value[0].string, true).then(
+                        function(keys){
+                            if(keys.length === 1){
+                                resolve(keys[0]);
                             } else {
-                                for (let i=0; i< result.keys.length; i++ ) {
-                                    if (result.keys[i].invalid === false) {
-                                        let k = createKey(
-                                            result.keys[i].fingerprint,
-                                            !prepare_sync,
-                                            result.keys[i]);
-                                        resolve(k);
-                                        break;
-                                    } else if (i === result.keys.length - 1){
-                                        reject(gpgme_error('KEY_NO_DEFAULT'));
-                                    }
-                                }
+                                reject(gpgme_error('KEY_NO_DEFAULT'));
                             }
                         }, function(error){
                             reject(error);
                         });
-                    }
-                }, function(error){
-                    reject(error);
-                });
+                } else {
+                    let msg = createMessage('keylist');
+                    msg.setParameter('secret', true);
+                    msg.post().then(function(result){
+                        if (result.keys.length === 0){
+                            reject(gpgme_error('KEY_NO_DEFAULT'));
+                        } else {
+                            for (let i=0; i< result.keys.length; i++ ) {
+                                if (result.keys[i].invalid === false) {
+                                    let k = createKey(
+                                        result.keys[i].fingerprint,
+                                        !prepare_sync,
+                                        result.keys[i]);
+                                    resolve(k);
+                                    break;
+                                } else if (i === result.keys.length - 1){
+                                    reject(gpgme_error('KEY_NO_DEFAULT'));
+                                }
+                            }
+                        }
+                    }, function(error){
+                        reject(error);
+                    });
+                }
+            }, function(error){
+                reject(error);
             });
-        };
+        });
+    }
 
-        /**
-         * @typedef {Object} importResult The result of a Key update
-         * @property {Object} summary Numerical summary of the result. See the
-         * feedbackValues variable for available Keys values and the gnupg
-         * documentation.
-         * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html
-         * for details on their meaning.
-         * @property {Array<importedKeyResult>} Keys Array of Object containing
-         * GPGME_Keys with additional import information
-         *
-         */
+    /**
+     * @typedef {Object} importResult The result of a Key update
+     * @property {Object} summary Numerical summary of the result. See the
+     * feedbackValues variable for available Keys values and the gnupg
+     * documentation.
+     * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html
+     * for details on their meaning.
+     * @property {Array<importedKeyResult>} Keys Array of Object containing
+     * GPGME_Keys with additional import information
+     *
+     */
 
-        /**
-         * @typedef {Object} importedKeyResult
-         * @property {GPGME_Key} key The resulting key
-         * @property {String} status:
-         *  'nochange' if the Key was not changed,
-         *  'newkey' if the Key was imported in gpg, and did not exist
-         *    previously,
-         *  'change' if the key existed, but details were updated. For details,
-         *    Key.changes is available.
-         * @property {Boolean} changes.userId Changes in userIds
-         * @property {Boolean} changes.signature Changes in signatures
-         * @property {Boolean} changes.subkey Changes in subkeys
-         */
+    /**
+     * @typedef {Object} importedKeyResult
+     * @property {GPGME_Key} key The resulting key
+     * @property {String} status:
+     *  'nochange' if the Key was not changed,
+     *  'newkey' if the Key was imported in gpg, and did not exist
+     *    previously,
+     *  'change' if the key existed, but details were updated. For details,
+     *    Key.changes is available.
+     * @property {Boolean} changes.userId Changes in userIds
+     * @property {Boolean} changes.signature Changes in signatures
+     * @property {Boolean} changes.subkey Changes in subkeys
+     */
 
-        /**
-         * Import an armored Key block into gnupg. Note that this currently
-         * will not succeed on private Key blocks.
-         * @param {String} armored Armored Key block of the Key(s) to be
-         * imported into gnupg
-         * @param {Boolean} prepare_sync prepare the keys for synched use
-         * (see {@link getKeys}).
-         * @returns {Promise<importResult>} A summary and Keys considered.
-         * @async
-         * @static
-         */
-        this.importKey = function (armored, prepare_sync) {
-            let feedbackValues = ['considered', 'no_user_id', 'imported',
-                'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys',
-                'new_signatures', 'new_revocations', 'secret_read',
-                'secret_imported', 'secret_unchanged', 'skipped_new_keys',
-                'not_imported', 'skipped_v3_keys'];
-            if (!armored || typeof(armored) !== 'string'){
-                return Promise.reject(gpgme_error('PARAM_WRONG'));
-            }
-            let me = this;
-            return new Promise(function(resolve, reject){
-                let msg = createMessage('import');
-                msg.setParameter('data', armored);
-                msg.post().then(function(response){
-                    let infos = {};
-                    let fprs = [];
-                    let summary = {};
-                    for (let i=0; i < feedbackValues.length; i++ ){
-                        summary[feedbackValues[i]] =
-                            response.result[feedbackValues[i]];
-                    }
-                    if (!response.result.hasOwnProperty('imports') ||
-                        response.result.imports.length === 0
-                    ){
-                        resolve({Keys:[],summary: summary});
-                        return;
+    /**
+     * Import an armored Key block into gnupg. Note that this currently
+     * will not succeed on private Key blocks.
+     * @param {String} armored Armored Key block of the Key(s) to be
+     * imported into gnupg
+     * @param {Boolean} prepare_sync prepare the keys for synched use
+     * (see {@link getKeys}).
+     * @returns {Promise<importResult>} A summary and Keys considered.
+     * @async
+     * @static
+     */
+    importKey (armored, prepare_sync) {
+        let feedbackValues = ['considered', 'no_user_id', 'imported',
+            'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys',
+            'new_signatures', 'new_revocations', 'secret_read',
+            'secret_imported', 'secret_unchanged', 'skipped_new_keys',
+            'not_imported', 'skipped_v3_keys'];
+        if (!armored || typeof(armored) !== 'string'){
+            return Promise.reject(gpgme_error('PARAM_WRONG'));
+        }
+        let me = this;
+        return new Promise(function(resolve, reject){
+            let msg = createMessage('import');
+            msg.setParameter('data', armored);
+            msg.post().then(function(response){
+                let infos = {};
+                let fprs = [];
+                let summary = {};
+                for (let i=0; i < feedbackValues.length; i++ ){
+                    summary[feedbackValues[i]] =
+                        response.result[feedbackValues[i]];
+                }
+                if (!response.result.hasOwnProperty('imports') ||
+                    response.result.imports.length === 0
+                ){
+                    resolve({Keys:[],summary: summary});
+                    return;
+                }
+                for (let res=0; res<response.result.imports.length; res++){
+                    let result = response.result.imports[res];
+                    let status = '';
+                    if (result.status === 0){
+                        status = 'nochange';
+                    } else if ((result.status & 1) === 1){
+                        status = 'newkey';
+                    } else {
+                        status = 'change';
                     }
-                    for (let res=0; res<response.result.imports.length; res++){
-                        let result = response.result.imports[res];
-                        let status = '';
-                        if (result.status === 0){
-                            status = 'nochange';
-                        } else if ((result.status & 1) === 1){
-                            status = 'newkey';
-                        } else {
-                            status = 'change';
-                        }
-                        let changes = {};
-                        changes.userId = (result.status & 2) === 2;
-                        changes.signature = (result.status & 4) === 4;
-                        changes.subkey = (result.status & 8) === 8;
-                        //16 new secret key: not implemented
+                    let changes = {};
+                    changes.userId = (result.status & 2) === 2;
+                    changes.signature = (result.status & 4) === 4;
+                    changes.subkey = (result.status & 8) === 8;
+                    //16 new secret key: not implemented
 
-                        fprs.push(result.fingerprint);
-                        infos[result.fingerprint] = {
-                            changes: changes,
-                            status: status
-                        };
-                    }
-                    let resultset = [];
-                    if (prepare_sync === true){
-                        me.getKeys(fprs, true).then(function(result){
-                            for (let i=0; i < result.length; i++) {
-                                resultset.push({
-                                    key: result[i],
-                                    changes:
-                                        infos[result[i].fingerprint].changes,
-                                    status: infos[result[i].fingerprint].status
-                                });
-                            }
-                            resolve({Keys:resultset,summary: summary});
-                        }, function(error){
-                            reject(error);
-                        });
-                    } else {
-                        for (let i=0; i < fprs.length; i++) {
+                    fprs.push(result.fingerprint);
+                    infos[result.fingerprint] = {
+                        changes: changes,
+                        status: status
+                    };
+                }
+                let resultset = [];
+                if (prepare_sync === true){
+                    me.getKeys(fprs, true).then(function(result){
+                        for (let i=0; i < result.length; i++) {
                             resultset.push({
-                                key: createKey(fprs[i]),
-                                changes: infos[fprs[i]].changes,
-                                status: infos[fprs[i]].status
+                                key: result[i],
+                                changes:
+                                    infos[result[i].fingerprint].changes,
+                                status: infos[result[i].fingerprint].status
                             });
                         }
-                        resolve({Keys:resultset,summary:summary});
+                        resolve({Keys:resultset,summary: summary});
+                    }, function(error){
+                        reject(error);
+                    });
+                } else {
+                    for (let i=0; i < fprs.length; i++) {
+                        resultset.push({
+                            key: createKey(fprs[i]),
+                            changes: infos[fprs[i]].changes,
+                            status: infos[fprs[i]].status
+                        });
                     }
+                    resolve({Keys:resultset,summary:summary});
+                }
 
-                }, function(error){
-                    reject(error);
-                });
+            }, function(error){
+                reject(error);
+            });
 
 
-            });
+        });
 
 
-        };
+    }
 
-        /**
-         * Convenience function for deleting a Key. See {@link Key.delete} for
-         * further information about the return values.
-         * @param {String} fingerprint
-         * @returns {Promise<Boolean|GPGME_Error>}
-         * @async
-         * @static
-         */
-        this.deleteKey = function(fingerprint){
-            if (isFingerprint(fingerprint) === true) {
-                let key = createKey(fingerprint);
-                return key.delete();
-            } else {
-                return Promise.reject(gpgme_error('KEY_INVALID'));
-            }
-        };
+    /**
+     * Convenience function for deleting a Key. See {@link Key.delete} for
+     * further information about the return values.
+     * @param {String} fingerprint
+     * @returns {Promise<Boolean|GPGME_Error>}
+     * @async
+     * @static
+     */
+    deleteKey(fingerprint){
+        if (isFingerprint(fingerprint) === true) {
+            let key = createKey(fingerprint);
+            return key.delete();
+        } else {
+            return Promise.reject(gpgme_error('KEY_INVALID'));
+        }
+    }
 
-        /**
-         * Generates a new Key pair directly in gpg, and returns a GPGME_Key
-         * representing that Key. Please note that due to security concerns,
-         * secret Keys can not be deleted or exported from inside gpgme.js.
-         *
-         * @param {String} userId The user Id, e.g. 'Foo Bar <foo@bar.baz>'
-         * @param {String} algo (optional) algorithm (and optionally key size)
-         * to be used. See {@link supportedKeyAlgos} below for supported
-         * values.
-         * @param {Date} expires (optional) Expiration date. If not set,
-         * expiration will be set to 'never'
-         *
-         * @return {Promise<Key|GPGME_Error>}
-         * @async
-         */
-        this.generateKey = function (userId, algo = 'default', expires){
-            if (
-                typeof(userId) !== 'string' ||
-                supportedKeyAlgos.indexOf(algo) < 0 ||
-                (expires && !(expires instanceof Date))
-            ){
-                return Promise.reject(gpgme_error('PARAM_WRONG'));
+    /**
+     * Generates a new Key pair directly in gpg, and returns a GPGME_Key
+     * representing that Key. Please note that due to security concerns,
+     * secret Keys can not be deleted or exported from inside gpgme.js.
+     *
+     * @param {String} userId The user Id, e.g. 'Foo Bar <foo@bar.baz>'
+     * @param {String} algo (optional) algorithm (and optionally key size)
+     * to be used. See {@link supportedKeyAlgos} below for supported
+     * values.
+     * @param {Date} expires (optional) Expiration date. If not set,
+     * expiration will be set to 'never'
+     *
+     * @return {Promise<Key|GPGME_Error>}
+     * @async
+     */
+    generateKey(userId, algo = 'default', expires){
+        if (
+            typeof(userId) !== 'string' ||
+            supportedKeyAlgos.indexOf(algo) < 0 ||
+            (expires && !(expires instanceof Date))
+        ){
+            return Promise.reject(gpgme_error('PARAM_WRONG'));
+        }
+        let me = this;
+        return new Promise(function(resolve, reject){
+            let msg = createMessage('createkey');
+            msg.setParameter('userid', userId);
+            msg.setParameter('algo', algo );
+            if (expires){
+                msg.setParameter('expires',
+                    Math.floor(expires.valueOf()/1000));
+            } else {
+                msg.setParameter('expires', 0);
             }
-            let me = this;
-            return new Promise(function(resolve, reject){
-                let msg = createMessage('createkey');
-                msg.setParameter('userid', userId);
-                msg.setParameter('algo', algo );
-                if (expires){
-                    msg.setParameter('expires',
-                        Math.floor(expires.valueOf()/1000));
-                } else {
-                    msg.setParameter('expires', 0);
-                }
-                msg.post().then(function(response){
-                    me.getKeys(response.fingerprint, true).then(
-                        // TODO prepare_sync?
-                        function(result){
-                            resolve(result);
-                        }, function(error){
-                            reject(error);
-                        });
-                }, function(error) {
-                    reject(error);
-                });
+            msg.post().then(function(response){
+                me.getKeys(response.fingerprint, true).then(
+                    // TODO prepare_sync?
+                    function(result){
+                        resolve(result);
+                    }, function(error){
+                        reject(error);
+                    });
+            }, function(error) {
+                reject(error);
             });
-        };
+        });
     }
 }
 
+
 /**
  * List of algorithms supported for key generation. Please refer to the gnupg
  * documentation for details
index e2c0734..2134fe9 100644 (file)
@@ -36,7 +36,7 @@ export function createMessage(operation){
         return gpgme_error('PARAM_WRONG');
     }
     if (permittedOperations.hasOwnProperty(operation)){
-        return Object.freeze(new GPGME_Message(operation));
+        return new GPGME_Message(operation);
     } else {
         return gpgme_error('MSG_WRONG_OP');
     }
@@ -52,199 +52,188 @@ export function createMessage(operation){
 export class GPGME_Message {
 
     constructor(operation){
-        let _msg = {
+        this._msg = {
             op: operation,
             chunksize: 1023* 1024
         };
-        let expected = null;
+        this._expected = null;
+    }
 
-        this.getOperation = function(){
-            return _msg.op;
-        };
+    get operation(){
+        return this._msg.op;
+    }
 
-        this.setExpect = function(value){
-            if (value === 'base64'){
-                expected = value;
-            }
-        };
-        this.getExpect = function(){
-            return expected;
-        };
+    set expected (value){
+        if (value === 'base64'){
+            this._expected = value;
+        }
+    }
 
-        /**
-         * 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.
-         */
-        this.setChunksize = function (value){
-            if (
-                Number.isInteger(value) &&
-                value > 10 * 1024 &&
-                value <= 1024 * 1024
-            ){
-                _msg.chunksize = 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;
+        }
+    }
 
-        this.getMsg = function(){
-            return _msg;
-        };
+    get chunksize(){
+        return this._msg.chunksize;
+    }
 
-        this.getChunksize= function() {
-            return _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
-         */
-        this.setParameter = function ( param,value ){
-            if (!param || typeof(param) !== 'string'){
+    /**
+     * 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'){
+            return gpgme_error('PARAM_WRONG');
+        }
+        let po = permittedOperations[this._msg.op];
+        if (!po){
+            return 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 {
+            return 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;
+                }
                 return gpgme_error('PARAM_WRONG');
-            }
-            let po = permittedOperations[_msg.op];
-            if (!po){
-                return 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 {
+            case 'number':
+                if (
+                    poparam.allowed.indexOf('number') >= 0
+                        && isNaN(value) === false){
+                    return true;
+                }
                 return 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;
+
+            case 'boolean':
+                if (poparam.allowed.indexOf('boolean') >= 0){
+                    return true;
+                }
+                return gpgme_error('PARAM_WRONG');
+            case 'object':
+                if (Array.isArray(val)){
+                    if (poparam.array_allowed !== true){
+                        return gpgme_error('PARAM_WRONG');
                     }
-                    return gpgme_error('PARAM_WRONG');
-                case 'number':
-                    if (
-                        poparam.allowed.indexOf('number') >= 0
-                            && isNaN(value) === false){
+                    for (let i=0; i < val.length; i++){
+                        let res = checktype(val[i]);
+                        if (res !== true){
+                            return res;
+                        }
+                    }
+                    if (val.length > 0) {
                         return true;
                     }
-                    return gpgme_error('PARAM_WRONG');
-
-                case 'boolean':
-                    if (poparam.allowed.indexOf('boolean') >= 0){
+                } else if (val instanceof Uint8Array){
+                    if (poparam.allowed.indexOf('Uint8Array') >= 0){
                         return true;
                     }
                     return gpgme_error('PARAM_WRONG');
-                case 'object':
-                    if (Array.isArray(val)){
-                        if (poparam.array_allowed !== true){
-                            return 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;
-                        }
-                        return gpgme_error('PARAM_WRONG');
-                    } else {
-                        return gpgme_error('PARAM_WRONG');
-                    }
-                    break;
-                default:
-                    return 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){
+                } else {
                     return gpgme_error('PARAM_WRONG');
                 }
+                break;
+            default:
+                return gpgme_error('PARAM_WRONG');
             }
-            _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.
-         */
-        this.isComplete = function(){
-            if (!_msg.op){
-                return false;
-            }
-            let reqParams = Object.keys(
-                permittedOperations[_msg.op].required);
-            let msg_params = Object.keys(_msg);
-            for (let i=0; i < reqParams.length; i++){
-                if (msg_params.indexOf(reqParams[i]) < 0){
-                    return false;
-                }
+        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');
             }
-            return true;
-        };
-        /**
-         * Sends the Message via nativeMessaging and resolves with the answer.
-         * @returns {Promise<Object|GPGME_Error>}
-         * @async
-         */
-        this.post = function(){
-            let me = this;
-            return new Promise(function(resolve, reject) {
-                if (me.isComplete() === true) {
-
-                    let conn  = Object.freeze(new Connection);
-                    conn.post(me).then(function(response) {
-                        resolve(response);
-                    }, function(reason) {
-                        reject(reason);
-                    });
-                }
-                else {
-                    reject(gpgme_error('MSG_INCOMPLETE'));
-                }
-            });
-        };
+        }
+        this._msg[param] = value;
+        return true;
     }
 
+
     /**
-     * Returns the prepared message with parameters and completeness checked
-     * @returns {Object|null} Object to be posted to gnupg, or null if
-     * incomplete
+     * 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.
      */
-    get message(){
-        if (this.isComplete() === true){
-            return this.getMsg();
+    isComplete(){
+        if (!this._msg.op){
+            return false;
         }
-        else {
-            return null;
+        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;
     }
-    get operation(){
-        return this.getOperation();
-    }
-    get chunksize(){
-        return this.getChunksize();
+    /**
+     * 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'));
+            }
+        });
     }
+
 }
index 55131b0..6536577 100644 (file)
@@ -66,7 +66,7 @@ export function createSignature(sigObject){
             }
         }
     }
-    return Object.freeze(new GPGME_Signature(sigObject));
+    return new GPGME_Signature(sigObject);
 }
 
 
@@ -82,101 +82,65 @@ export function createSignature(sigObject){
 class GPGME_Signature {
 
     constructor(sigObject){
-        let _rawSigObject = sigObject;
-
-        this.getFingerprint = function(){
-            if (!_rawSigObject.fingerprint){
-                return gpgme_error('SIG_WRONG');
-            } else {
-                return _rawSigObject.fingerprint;
-            }
-        };
-
-        /**
-         * The expiration of this Signature as Javascript date, or null if
-         * signature does not expire
-         * @returns {Date | null}
-         */
-        this.getExpiration = function(){
-            if (!_rawSigObject.exp_timestamp){
-                return null;
-            }
-            return new Date(_rawSigObject.exp_timestamp* 1000);
-        };
-
-        /**
-         * The creation date of this Signature in Javascript Date
-         * @returns {Date}
-         */
-        this.getTimestamp= function (){
-            return new Date(_rawSigObject.timestamp * 1000);
-        };
-
-        /**
-         * The overall validity of the key. If false, errorDetails may contain
-         * additional information.
-         */
-        this.getValid= function() {
-            if (_rawSigObject.summary.valid === true){
-                return true;
-            } else {
-                return false;
-            }
-        };
-
-        /**
-         * gives more information on non-valid signatures. Refer to the gpgme
-         * docs https://www.gnupg.org/documentation/manuals/gpgme/Verify.html
-         * for details on the values.
-         * @returns {Object} Object with boolean properties
-         */
-        this.getErrorDetails = function (){
-            let properties = ['revoked', 'key-expired', 'sig-expired',
-                'key-missing', 'crl-missing', 'crl-too-old', 'bad-policy',
-                'sys-error'];
-            let result = {};
-            for (let i=0; i< properties.length; i++){
-                if ( _rawSigObject.hasOwnProperty(properties[i]) ){
-                    result[properties[i]] = _rawSigObject[properties[i]];
-                }
-            }
-            return result;
-        };
+        this._rawSigObject = sigObject;
     }
-
-    /**
-     * Convenience getter for {@link getFingerprint}
-     */
     get fingerprint(){
-        return this.getFingerprint();
+        if (!this._rawSigObject.fingerprint){
+            return gpgme_error('SIG_WRONG');
+        } else {
+            return this._rawSigObject.fingerprint;
+        }
     }
 
     /**
-     * Convenience getter for {@link getExpiration}
+     * The expiration of this Signature as Javascript date, or null if
+     * signature does not expire
+     * @returns {Date | null}
      */
     get expiration(){
-        return this.getExpiration();
+        if (!this._rawSigObject.exp_timestamp){
+            return null;
+        }
+        return new Date(this._rawSigObject.exp_timestamp* 1000);
     }
 
     /**
-     * Convenience getter for {@link getTimeStamp}
+     * The creation date of this Signature in Javascript Date
+     * @returns {Date}
      */
-    get timestamp(){
-        return this.getTimestamp();
+    get timestamp (){
+        return new Date(this._rawSigObject.timestamp * 1000);
     }
 
     /**
-     * Convenience getter for {@link getValid}
+     * The overall validity of the key. If false, errorDetails may contain
+     * additional information.
      */
-    get valid(){
-        return this.getValid();
+    get valid () {
+        if (this._rawSigObject.summary.valid === true){
+            return true;
+        } else {
+            return false;
+        }
     }
 
     /**
-     * Convenience getter for {@link getErrorDetails}
+     * gives more information on non-valid signatures. Refer to the gpgme
+     * docs https://www.gnupg.org/documentation/manuals/gpgme/Verify.html
+     * for details on the values.
+     * @returns {Object} Object with boolean properties
      */
     get errorDetails(){
-        return this.getErrorDetails();
+        let properties = ['revoked', 'key-expired', 'sig-expired',
+            'key-missing', 'crl-missing', 'crl-too-old', 'bad-policy',
+            'sys-error'];
+        let result = {};
+        for (let i=0; i< properties.length; i++){
+            if ( this._rawSigObject.hasOwnProperty(properties[i]) ){
+                result[properties[i]] = this._rawSigObject[properties[i]];
+            }
+        }
+        return result;
     }
 }
 
index 1816436..4aa5175 100644 (file)
@@ -85,243 +85,226 @@ import { createSignature } from './Signature';
 export class GpgME {
 
     constructor(){
-        let _Keyring = null;
-
-        /**
-         * Sets a new Keyring to be used
-         * @param {GPGME_Keyring} keyring
-         */
-        this.setKeyring = function(keyring){
-            if (keyring && keyring instanceof GPGME_Keyring){
-                _Keyring = keyring;
-            }
-        };
+        this._Keyring = null;
+    }
 
-        /**
-         * Accesses the {@link GPGME_Keyring}.
-         */
-        this.getKeyring = function(){
-            if (!_Keyring){
-                _Keyring = Object.freeze(new GPGME_Keyring);
-            }
-            return _Keyring;
-        };
+    /**
+     * setter for {@link setKeyring}.
+     * @param {GPGME_Keyring} keyring A Keyring to use
+     */
+    set Keyring(keyring){
+        if (keyring && keyring instanceof GPGME_Keyring){
+            this._Keyring = keyring;
+        }
+    }
+    /**
+     * Accesses the {@link GPGME_Keyring}.
+     */
+    get Keyring(){
+        if (!this._Keyring){
+            this._Keyring = new GPGME_Keyring;
+        }
+        return this._Keyring;
+    }
 
-        /**
-         * Encrypt (and optionally sign) data
-         * @param {String|Object} data text/data to be encrypted as String. Also
-         * accepts Objects with a getText method
-         * @param {inputKeys} publicKeys
-         * Keys used to encrypt the message
-         * @param {inputKeys} secretKeys (optional) Keys used to sign the
-         * message. If Keys are present, the  operation requested is assumed
-         * to be 'encrypt and sign'
-         * @param {Boolean} base64 (optional) The data will be interpreted as
-         * base64 encoded data.
-         * @param {Boolean} armor (optional) Request the output as armored
-         * block.
-         * @param {Boolean} wildcard (optional) If true, recipient information
-         * will not be added to the message.
-         * @param {Object} additional use additional valid gpg options as
-         * defined in {@link permittedOperations}
-         * @returns {Promise<encrypt_result>} Object containing the encrypted
-         * message and additional info.
-         * @async
-         */
-        this.encrypt = function (data, publicKeys, secretKeys, base64=false,
-            armor=true, wildcard=false, additional = {}
-        ){
-            let msg = createMessage('encrypt');
-            if (msg instanceof Error){
-                return Promise.reject(msg);
-            }
-            msg.setParameter('armor', armor);
-            msg.setParameter('always-trust', true);
-            if (base64 === true) {
-                msg.setParameter('base64', true);
-            }
-            let pubkeys = toKeyIdArray(publicKeys);
-            msg.setParameter('keys', pubkeys);
-            let sigkeys = toKeyIdArray(secretKeys);
-            if (sigkeys.length > 0) {
-                msg.setParameter('signing_keys', sigkeys);
-            }
-            putData(msg, data);
-            if (wildcard === true){
-                msg.setParameter('throw-keyids', true);
-            }
-            if (additional){
-                let additional_Keys = Object.keys(additional);
-                for (let k = 0; k < additional_Keys.length; k++) {
-                    msg.setParameter(additional_Keys[k],
-                        additional[additional_Keys[k]]);
-                }
-            }
-            if (msg.isComplete() === true){
-                return msg.post();
-            } else {
-                return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+    /**
+     * Encrypt (and optionally sign) data
+     * @param {String|Object} data text/data to be encrypted as String. Also
+     * accepts Objects with a getText method
+     * @param {inputKeys} publicKeys
+     * Keys used to encrypt the message
+     * @param {inputKeys} secretKeys (optional) Keys used to sign the
+     * message. If Keys are present, the  operation requested is assumed
+     * to be 'encrypt and sign'
+     * @param {Boolean} base64 (optional) The data will be interpreted as
+     * base64 encoded data.
+     * @param {Boolean} armor (optional) Request the output as armored
+     * block.
+     * @param {Boolean} wildcard (optional) If true, recipient information
+     * will not be added to the message.
+     * @param {Object} additional use additional valid gpg options as
+     * defined in {@link permittedOperations}
+     * @returns {Promise<encrypt_result>} Object containing the encrypted
+     * message and additional info.
+     * @async
+     */
+    encrypt (data, publicKeys, secretKeys, base64=false, armor=true,
+        wildcard=false, additional = {}){
+        let msg = createMessage('encrypt');
+        if (msg instanceof Error){
+            return Promise.reject(msg);
+        }
+        msg.setParameter('armor', armor);
+        msg.setParameter('always-trust', true);
+        if (base64 === true) {
+            msg.setParameter('base64', true);
+        }
+        let pubkeys = toKeyIdArray(publicKeys);
+        msg.setParameter('keys', pubkeys);
+        let sigkeys = toKeyIdArray(secretKeys);
+        if (sigkeys.length > 0) {
+            msg.setParameter('signing_keys', sigkeys);
+        }
+        putData(msg, data);
+        if (wildcard === true){
+            msg.setParameter('throw-keyids', true);
+        }
+        if (additional){
+            let additional_Keys = Object.keys(additional);
+            for (let k = 0; k < additional_Keys.length; k++) {
+                msg.setParameter(additional_Keys[k],
+                    additional[additional_Keys[k]]);
             }
-        };
+        }
+        if (msg.isComplete() === true){
+            return msg.post();
+        } else {
+            return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+        }
+    }
 
-        /**
-        * Decrypts a Message
-        * @param {String|Object} data text/data to be decrypted. Accepts
-        * Strings and Objects with a getText method
-        * @param {Boolean} base64 (optional) false if the data is an armored
-        * block, true if it is base64 encoded binary data
-        * @returns {Promise<decrypt_result>} Decrypted Message and information
-        * @async
-        */
-        this.decrypt = function (data, base64=false){
-            if (data === undefined){
-                return Promise.reject(gpgme_error('MSG_EMPTY'));
-            }
-            let msg = createMessage('decrypt');
+    /**
+    * Decrypts a Message
+    * @param {String|Object} data text/data to be decrypted. Accepts
+    * Strings and Objects with a getText method
+    * @param {Boolean} base64 (optional) false if the data is an armored
+    * block, true if it is base64 encoded binary data
+    * @returns {Promise<decrypt_result>} Decrypted Message and information
+    * @async
+    */
+    decrypt (data, base64=false){
+        if (data === undefined){
+            return Promise.reject(gpgme_error('MSG_EMPTY'));
+        }
+        let msg = createMessage('decrypt');
 
-            if (msg instanceof Error){
-                return Promise.reject(msg);
-            }
-            if (base64 === true){
-                msg.setParameter('base64', true);
-            }
-            putData(msg, data);
-            if (base64 === true){
-                msg.setParameter('base64', true);
-            }
-            return new Promise(function(resolve, reject){
-                msg.post().then(function(result){
-                    let _result = {data: result.data};
-                    _result.base64 = result.base64 ? true: false;
-                    _result.is_mime = result.is_mime ? true: false;
-                    if (result.file_name){
-                        _result.file_name = result.file_name;
-                    } else {
-                        _result.file_name = null;
-                    }
-                    if (
-                        result.hasOwnProperty('signatures') &&
-                        Array.isArray(result.signatures)
-                    ) {
-                        _result.signatures = collectSignatures(
-                            result.signatures);
-                    }
-                    resolve(_result);
-                }, function(error){
-                    reject(error);
-                });
+        if (msg instanceof Error){
+            return Promise.reject(msg);
+        }
+        if (base64 === true){
+            msg.setParameter('base64', true);
+        }
+        putData(msg, data);
+        if (base64 === true){
+            msg.setParameter('base64', true);
+        }
+        return new Promise(function(resolve, reject){
+            msg.post().then(function(result){
+                let _result = {data: result.data};
+                _result.base64 = result.base64 ? true: false;
+                _result.is_mime = result.is_mime ? true: false;
+                if (result.file_name){
+                    _result.file_name = result.file_name;
+                } else {
+                    _result.file_name = null;
+                }
+                if (
+                    result.hasOwnProperty('signatures') &&
+                    Array.isArray(result.signatures)
+                ) {
+                    _result.signatures = collectSignatures(
+                        result.signatures);
+                }
+                resolve(_result);
+            }, function(error){
+                reject(error);
             });
-        };
+        });
+    }
 
-        /**
-         * Sign a Message
-         * @param {String|Object} data text/data to be signed. Accepts Strings
-         * and Objects with a getText method.
-         * @param {inputKeys} keys The key/keys to use for signing
-         * @param {String} mode The signing mode. Currently supported:
-         *  'clearsign':The Message is embedded into the signature;
-         *  'detached': The signature is stored separately
-         * @param {Boolean} base64 input is considered base64
-         * @returns {Promise<signResult>}
-         * @async
-         */
-        this.sign = function (data, keys, mode='clearsign', base64=false) {
-            if (data === undefined){
-                return Promise.reject(gpgme_error('MSG_EMPTY'));
-            }
-            let key_arr = toKeyIdArray(keys);
-            if (key_arr.length === 0){
-                return Promise.reject(gpgme_error('MSG_NO_KEYS'));
-            }
-            let msg = createMessage('sign');
+    /**
+     * Sign a Message
+     * @param {String|Object} data text/data to be signed. Accepts Strings
+     * and Objects with a getText method.
+     * @param {inputKeys} keys The key/keys to use for signing
+     * @param {String} mode The signing mode. Currently supported:
+     *  'clearsign':The Message is embedded into the signature;
+     *  'detached': The signature is stored separately
+     * @param {Boolean} base64 input is considered base64
+     * @returns {Promise<signResult>}
+     * @async
+     */
+    sign (data, keys, mode='clearsign', base64=false) {
+        if (data === undefined){
+            return Promise.reject(gpgme_error('MSG_EMPTY'));
+        }
+        let key_arr = toKeyIdArray(keys);
+        if (key_arr.length === 0){
+            return Promise.reject(gpgme_error('MSG_NO_KEYS'));
+        }
+        let msg = createMessage('sign');
 
-            msg.setParameter('keys', key_arr);
-            if (base64 === true){
-                msg.setParameter('base64', true);
+        msg.setParameter('keys', key_arr);
+        if (base64 === true){
+            msg.setParameter('base64', true);
+        }
+        msg.setParameter('mode', mode);
+        putData(msg, data);
+        return new Promise(function(resolve,reject) {
+            if (mode ==='detached'){
+                msg.expected ='base64';
             }
-            msg.setParameter('mode', mode);
-            putData(msg, data);
-            return new Promise(function(resolve,reject) {
-                if (mode ==='detached'){
-                    msg.setExpect('base64');
+            msg.post().then( function(message) {
+                if (mode === 'clearsign'){
+                    resolve({
+                        data: message.data}
+                    );
+                } else if (mode === 'detached') {
+                    resolve({
+                        data: data,
+                        signature: message.data
+                    });
                 }
-                msg.post().then( function(message) {
-                    if (mode === 'clearsign'){
-                        resolve({
-                            data: message.data}
-                        );
-                    } else if (mode === 'detached') {
-                        resolve({
-                            data: data,
-                            signature: message.data
-                        });
-                    }
-                }, function(error){
-                    reject(error);
-                });
+            }, function(error){
+                reject(error);
             });
-        };
+        });
+    }
 
-        /**
-         * Verifies data.
-         * @param {String|Object} data text/data to be verified. Accepts Strings
-         * and Objects with a getText method
-         * @param {String} (optional) A detached signature. If not present,
-         * opaque mode is assumed
-         * @param {Boolean} (optional) Data and signature are base64 encoded
-         * @returns {Promise<verifyResult>}
-         *@async
-        */
-        this.verify= function (data, signature, base64 = false){
-            let msg = createMessage('verify');
-            let dt = putData(msg, data);
-            if (dt instanceof Error){
-                return Promise.reject(dt);
+    /**
+     * Verifies data.
+     * @param {String|Object} data text/data to be verified. Accepts Strings
+     * and Objects with a getText method
+     * @param {String} (optional) A detached signature. If not present,
+     * opaque mode is assumed
+     * @param {Boolean} (optional) Data and signature are base64 encoded
+     * @returns {Promise<verifyResult>}
+     *@async
+    */
+    verify (data, signature, base64 = false){
+        let msg = createMessage('verify');
+        let dt = putData(msg, data);
+        if (dt instanceof Error){
+            return Promise.reject(dt);
+        }
+        if (signature){
+            if (typeof(signature)!== 'string'){
+                return Promise.reject(gpgme_error('PARAM_WRONG'));
+            } else {
+                msg.setParameter('signature', signature);
             }
-            if (signature){
-                if (typeof(signature)!== 'string'){
-                    return Promise.reject(gpgme_error('PARAM_WRONG'));
+        }
+        if (base64 === true){
+            msg.setParameter('base64', true);
+        }
+        return new Promise(function(resolve, reject){
+            msg.post().then(function (message){
+                if (!message.info || !message.info.signatures){
+                    reject(gpgme_error('SIG_NO_SIGS'));
                 } else {
-                    msg.setParameter('signature', signature);
-                }
-            }
-            if (base64 === true){
-                msg.setParameter('base64', true);
-            }
-            return new Promise(function(resolve, reject){
-                msg.post().then(function (message){
-                    if (!message.info || !message.info.signatures){
-                        reject(gpgme_error('SIG_NO_SIGS'));
-                    } else {
-                        let _result = collectSignatures(
-                            message.info.signatures);
-                        _result.is_mime = message.info.is_mime? true: false;
-                        if (message.info.filename){
-                            _result.file_name = message.info.filename;
-                        }
-                        _result.data = message.data;
-                        resolve(_result);
+                    let _result = collectSignatures(
+                        message.info.signatures);
+                    _result.is_mime = message.info.is_mime? true: false;
+                    if (message.info.filename){
+                        _result.file_name = message.info.filename;
                     }
-                }, function(error){
-                    reject(error);
-                });
+                    _result.data = message.data;
+                    resolve(_result);
+                }
+            }, function(error){
+                reject(error);
             });
-        };
-    }
-
-    /**
-     * setter for {@link setKeyring}.
-     * @param {GPGME_Keyring} keyring A Keyring to use
-     */
-    set Keyring(keyring){
-        this.setKeyring(keyring);
-    }
-
-    /**
-     * Accesses the {@link GPGME_Keyring}.
-     */
-    get Keyring(){
-        return this.getKeyring();
+        });
     }
 }
 
index 51f0753..ad4b05b 100644 (file)
@@ -34,11 +34,11 @@ import { Connection } from './Connection';
  */
 function init(){
     return new Promise(function(resolve, reject){
-        const connection = Object.freeze(new Connection);
+        const connection = new Connection;
         connection.checkConnection(false).then(
             function(result){
                 if (result === true) {
-                    resolve(Object.freeze(new GpgME()));
+                    resolve(new GpgME());
                 } else {
                     reject(gpgme_error('CONN_NO_CONNECT'));
                 }
@@ -48,5 +48,5 @@ function init(){
     });
 }
 
-const exportvalue = Object.freeze({init:init});
+const exportvalue = {init:init};
 export default exportvalue;
\ No newline at end of file
index 25023bc..47eeabf 100644 (file)
@@ -27,7 +27,7 @@ import { key_params as kp } from './unittest_inputvalues';
 import { Connection } from './src/Connection';
 import { gpgme_error } from './src/Errors';
 import { toKeyIdArray , isFingerprint } from './src/Helpers';
-import { GPGME_Key , createKey } from './src/Key';
+import { createKey } from './src/Key';
 import { GPGME_Keyring } from './src/Keyring';
 import {GPGME_Message, createMessage} from './src/Message';
 
@@ -116,14 +116,6 @@ function unittests (){
             expect(test0).to.include(hp.validFingerprint);
         });
 
-        it('correct GPGME_Key', function(){
-            expect(hp.validGPGME_Key).to.be.an.instanceof(GPGME_Key);
-            let test0 = toKeyIdArray(hp.validGPGME_Key);
-
-            expect(test0).to.be.an('array');
-            expect(test0).to.include(hp.validGPGME_Key.fingerprint);
-        });
-
         it('openpgpjs-like object', function(){
             let test0 = toKeyIdArray(hp.valid_openpgplike);
 
@@ -169,15 +161,9 @@ function unittests (){
     });
 
     describe('GPGME_Key', function(){
-
-        it('correct Key initialization', function(){
-            let key = createKey(kp.validKeyFingerprint);
-            expect(key).to.be.an.instanceof(GPGME_Key);
-        });
         it('Key has data after a first refresh', function(done) {
             let key = createKey(kp.validKeyFingerprint);
             key.refreshKey().then(function(key2){
-                expect(key2).to.be.an.instanceof(GPGME_Key);
                 expect(key2.get).to.be.a('function');
                 for (let i=0; i < kp.validKeyProperties.length; i++) {
                     let prop = key2.get(kp.validKeyProperties[i]);
@@ -220,7 +206,6 @@ function unittests (){
 
         it('Non-cached key async hasSecret (no secret in Key)', function (done){
             let key = createKey(kp.validFingerprintNoSecret, true);
-            expect(key).to.be.an.instanceof(GPGME_Key);
             key.get('hasSecret').then(function(result){
                 expect(result).to.be.a('boolean');
                 expect(result).to.equal(false);
@@ -246,32 +231,21 @@ function unittests (){
             }
         });
 
-        it('malformed GPGME_Key cannot be used', function(){
-            for (let i=0; i < 4; i++){
-                let key = new GPGME_Key(wp.four_invalid_params[i]);
-                expect(key.fingerprint).to.be.an.instanceof(Error);
-                expect(key.fingerprint.code).to.equal('KEY_INVALID');
-            }
-        });
-
-        it('Overwriting getFingerprint does not work', function(){
-            const evilFunction = function(){
-                return 'bad Data';
-            };
-            let key = createKey(kp.validKeyFingerprint, true);
-            expect(key.fingerprint).to.equal(kp.validKeyFingerprint);
-            try {
-                key.getFingerprint = evilFunction;
-            }
-            catch(e) {
-                expect(e).to.be.an.instanceof(TypeError);
-            }
-            expect(key.fingerprint).to.equal(kp.validKeyFingerprint);
-            expect(key.getFingerprint).to.not.equal(evilFunction);
-        });
-        // TODO: tests for subkeys
-        // TODO: tests for userids
-        // TODO: some invalid tests for key/keyring
+    //     it('Overwriting getFingerprint does not work', function(){
+    //         const evilFunction = function(){
+    //             return 'bad Data';
+    //         };
+    //         let key = createKey(kp.validKeyFingerprint, true);
+    //         expect(key.fingerprint).to.equal(kp.validKeyFingerprint);
+    //         try {
+    //             key.getFingerprint = evilFunction;
+    //         }
+    //         catch(e) {
+    //             expect(e).to.be.an.instanceof(TypeError);
+    //         }
+    //         expect(key.fingerprint).to.equal(kp.validKeyFingerprint);
+    //         expect(key.getFingerprint).to.not.equal(evilFunction);
+    //     });
     });
 
     describe('GPGME_Keyring', function(){
@@ -287,10 +261,7 @@ function unittests (){
                 let keyring = new GPGME_Keyring;
                 keyring.getKeys(null, true).then(function(result){
                     expect(result).to.be.an('array');
-                    expect(result[0]).to.be.an.instanceof(GPGME_Key);
                     expect(result[0].get('hasSecret')).to.be.a('boolean');
-                    // expect(result[0].get('armored')).to.include(
-                    //     '-----END PGP PUBLIC KEY BLOCK-----');
                     done();
                 });
             }
@@ -302,7 +273,6 @@ function unittests (){
                 keyring.getKeys(kp.validKeyFingerprint, true).then(
                     function(result){
                         expect(result).to.be.an('array');
-                        expect(result[0]).to.be.an.instanceof(GPGME_Key);
                         expect(result[0].get('hasSecret')).to.be.a('boolean');
                         done();
                     }