js: Configuration and Error handling
[gpgme.git] / lang / js / src / gpgmejs.js
1 /* gpgme.js - Javascript integration for gpgme
2  * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  * SPDX-License-Identifier: LGPL-2.1+
19  */
20
21 import {Connection} from "./Connection"
22 import {GPGME_Message} from './Message'
23 import {toKeyIdArray} from "./Helpers"
24 import { gpgme_error } from "./Errors"
25 import { GPGME_Keyring } from "./Keyring";
26
27 export class GpgME {
28     /**
29      * initializes GpgME by opening a nativeMessaging port
30      * TODO: add configuration
31      */
32     constructor(connection){
33         this.connection = connection;
34     }
35
36     set connection(connection){
37         if (this._connection instanceof Connection){
38             gpgme_error('CONN_ALREADY_CONNECTED');
39         }
40         if (connection instanceof Connection){
41             this._connection = connection;
42         } else {
43             gpgme_error('PARAM_WRONG');
44         }
45     }
46
47     get connection(){
48         if (this._connection instanceof Connection){
49             if (this._connection.isConnected){
50                 return this._connection;
51             }
52             return undefined; //TODO: connection was lost!
53         }
54         return undefined; //TODO: no connection there
55     }
56
57     set Keyring(keyring){
58         if (ring && ring instanceof GPGME_Keyring){
59             this._Keyring = ring;
60         }
61     }
62
63     get Keyring(){
64         return this._Keyring;
65     }
66
67     /**
68      * @param {String|Uint8Array} data text/data to be encrypted as String/Uint8Array
69      * @param  {GPGME_Key|String|Array<String>|Array<GPGME_Key>} publicKeys Keys used to encrypt the message
70      * @param {Boolean} wildcard (optional) If true, recipient information will not be added to the message
71      */
72     encrypt(data, publicKeys, wildcard=false){
73
74         let msg = new GPGME_Message('encrypt');
75
76         // TODO temporary
77         msg.setParameter('armor', true);
78         msg.setParameter('always-trust', true);
79
80         let pubkeys = toKeyIdArray(publicKeys);
81         msg.setParameter('keys', pubkeys);
82
83         putData(msg, data);
84         if (wildcard === true){msg.setParameter('throw-keyids', true);
85         };
86
87         return (this.connection.post(msg));
88     }
89
90     /**
91     * @param  {String} data TODO Format: base64? String? Message with the encrypted data
92     * @returns {Promise<Object>} decrypted message:
93         data:   The decrypted data.  This may be base64 encoded.
94         base64: Boolean indicating whether data is base64 encoded.
95         mime:   A Boolean indicating whether the data is a MIME object.
96         info:   An optional object with extra information.
97     * @async
98     */
99
100     decrypt(data){
101         if (data === undefined){
102             return Promise.reject(gpgme_error('MSG_EMPTY'));
103         }
104         let msg = new GPGME_Message('decrypt');
105         putData(msg, data);
106         return this.connection.post(msg);
107
108     }
109
110     deleteKey(key, delete_secret = false, no_confirm = false){
111         return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
112         let msg = new GPGME_Message('deletekey');
113         let key_arr = toKeyIdArray(key);
114         if (key_arr.length !== 1){
115             throw('TODO');
116             //should always be ONE key
117         }
118         msg.setParameter('key', key_arr[0]);
119         if (delete_secret === true){
120             msg.setParameter('allow_secret', true); //TBD
121         }
122         if (no_confirm === true){ //TODO: Do we want this hidden deep in the code?
123             msg.setParameter('delete_force', true); //TBD
124         }
125         this.connection.post(msg).then(function(success){
126             //TODO: it seems that there is always errors coming back:
127         }, function(error){
128             switch (error.msg){
129             case 'ERR_NO_ERROR':
130                 return Promise.resolve('okay'); //TBD
131             default:
132                 return Promise.reject(gpgme_error('TODO') ); //
133                 // INV_VALUE,
134                 // GPG_ERR_NO_PUBKEY,
135                 // GPG_ERR_AMBIGUOUS_NAME,
136                 // GPG_ERR_CONFLICT
137             }
138         });
139     }
140 }
141
142 /**
143  * Sets the data of the message, converting Uint8Array to base64 and setting
144  * the base64 flag
145  * @param {GPGME_Message} message The message where this data will be set
146  * @param {*} data The data to enter
147  * @param {String} propertyname // TODO unchecked still
148  */
149 function putData(message, data){
150     if (!message || !message instanceof GPGME_Message ) {
151         return gpgme_error('PARAM_WRONG');
152     }
153     if (!data){
154         message.setParameter('data', '');
155     } else if (data instanceof Uint8Array){
156         let decoder = new TextDecoder('utf8');
157         message.setParameter('base64', true);
158         message.setParameter ('data', decoder.decode(data));
159     } else if (typeof(data) === 'string') {
160         message.setParameter('base64', false);
161         message.setParameter('data', data);
162     } else if ( typeof(data) === 'object' && data.hasOwnProperty(getText)){
163         let txt = data.getText();
164         if (txt instanceof Uint8Array){
165             let decoder = new TextDecoder('utf8');
166             message.setParameter('base64', true);
167             message.setParameter ('data', decoder.decode(txt));
168         }
169     } else {
170         return gpgme_error('PARAM_WRONG');
171     }
172 }