From de0dc0bb86f79234159d32684003bb205240e07c Mon Sep 17 00:00:00 2001 From: Sukhbir Singh Date: Sun, 17 Dec 2017 12:05:35 -0500 Subject: [feat] Display status of bitmaskd in status bar Display the status of bitmaskd in Thunderbird's status bar by connecting to it using bitmask.js --- chrome/content/bitmask.js | 547 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 547 insertions(+) create mode 100644 chrome/content/bitmask.js (limited to 'chrome/content/bitmask.js') diff --git a/chrome/content/bitmask.js b/chrome/content/bitmask.js new file mode 100644 index 0000000..6ab87ff --- /dev/null +++ b/chrome/content/bitmask.js @@ -0,0 +1,547 @@ +// bitmask.js +// Copyright (C) 2016 LEAP +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/** + * bitmask object + * + * Contains all the bitmask API mapped by sections + * - user. User management like login, creation, ... + * - mail. Email service control. + * - keys. Keyring operations. + * - events. For registering to events. + * + * Every function returns a Promise that will be triggered once the request is + * finished or will fail if there was any error. Errors are always user readable + * strings. + */ + +try { + // Use Promises in non-ES6 compliant engines. + eval('import "babel-polyfill";') +} +catch (err) {} + +var bitmask = function(){ + var event_handlers = {}; + var api_url = '/API/'; + var api_token = null; + var last_uid = null; + var last_uuid = null; + + if (window.location.protocol === "file:") { + api_url = 'http://localhost:7070/API/'; + } + if (window.location.hash) { + api_token = window.location.hash.replace('#', '') + } + + // If the script is running from a Firefox (or Thunderbird) extension, get + // the api_token from ~/.config/leap/authtoken, and also set the api_url. + if (window.location.protocol === "chrome:") { + // Use the correct URL for the API. + api_url = 'http://localhost:7070/API/'; + + // Now fetch the token file and set api_token. + Components.utils.import("resource://gre/modules/osfile.jsm") + + let tokenPath = OS.Path.join(OS.Constants.Path.homeDir, ".config", "leap", "authtoken") + let decoder = new TextDecoder(); + + setInterval(function get_token_file() { + let promise = OS.File.read(tokenPath); + promise = promise.then(array => { + api_token = decoder.decode(array); + }, ex => { + api_token = null; + }); + return get_token_file; + }(), 3000); + } + + function call(command) { + var url = api_url + command.slice(0, 3).join('/'); + var data = JSON.stringify(command.slice(3)); + + return new Promise(function(resolve, reject) { + var req = new XMLHttpRequest(); + + req.open('POST', url); + if (api_token) { + req.setRequestHeader("X-Bitmask-Auth", api_token) + } + + req.onload = function() { + if (req.status == 200) { + parseResponse(req.response, resolve, reject); + } + else { + reject(Error(req.statusText)); + } + }; + + req.onerror = function() { + reject(Error("Network Error")); + }; + + req.send(data); + }); + }; + + function parseResponse(raw_response, resolve, reject) { + var response = JSON.parse(raw_response); + if (response.error === null) { + resolve(response.result); + } else { + reject(response.error); + } + }; + + function event_polling() { + if (api_token) { + call(['events', 'poll']).then(function(response) { + if (response !== null) { + var event = response[0]; + var content = response[1]; + if (event in event_handlers) { + Object.values(event_handlers[event]).forEach(function(handler) { + handler(event, content); + }) + } + } + event_polling(); + }, function(error) { + setTimeout(event_polling, 5000); + }); + } + }; + event_polling(); + + function private_str(priv) { + if (priv) { + return 'private' + } + return 'public' + }; + + return { + api_token: function() {return api_token}, + + core: { + /** + * Get bitmaskd version + * + * @return {Promise} {'version_core': str} + */ + version: function() { + return call(['core', 'version']); + }, + + /** + * Stop bitmaskd + */ + stop: function() { + return call(['core', 'stop']); + }, + + /** + * Get bitmaskd status + */ + status: function() { + return call(['core', 'status']); + } + }, + + bonafide: { + provider: { + create: function(domain) { + return call(['bonafide', 'provider', 'create', domain]); + }, + + read: function(domain, service) { + if (typeof service !== 'string') { + service = ""; + } + return call(['bonafide', 'provider', 'read', domain, service]); + }, + + delete: function(domain) { + return call(['bonafide', 'provider', 'delete', domain]); + }, + + list: function(seeded) { + if (typeof seeded !== 'boolean') { + seeded = false; + } + return call(['bonafide', 'provider', 'list', seeded]); + } + }, + + /** + * uids are of the form user@provider.net + */ + user: { + /** + * Register a new user + * + * @param {string} uid The uid to be created + * @param {string} password The user password + * @param {boolean} autoconf If the provider should be autoconfigured if it's not already known + * If it's not provided it will default to false + */ + create: function(uid, password, invite, autoconf) { + if (typeof autoconf !== 'boolean') { + autoconf = false; + } + return call(['bonafide', 'user', 'create', uid, password, invite, autoconf]); + }, + + /** + * Login + * + * @param {string} uid The uid to log in + * @param {string} password The user password + * @param {boolean} autoconf If the provider should be autoconfigured if it's not already known + * If it's not provided it will default to false + */ + auth: function(uid, password, autoconf) { + if (typeof autoconf !== 'boolean') { + autoconf = false; + } + return call(['bonafide', 'user', 'authenticate', uid, password, autoconf]).then(function(response) { + last_uuid = response.uuid + last_uid = uid + return response; + }); + }, + + /** + * Logout + * + * @param {string} uid The uid to log out. + */ + logout: function(uid) { + return call(['bonafide', 'user', 'logout', uid]); + }, + + /** + * List users + * + * @return {Promise} [{'userid': str, 'authenticated': boolean}] + */ + list: function() { + return call(['bonafide', 'user', 'list']); + }, + + /** + * Change password + * + * @param {string} uid The uid to log in + * @param {string} current_password The current user password + * @param {string} new_password The new user password + */ + update: function(uid, current_password, new_password) { + return call(['bonafide', 'user', 'update', uid, current_password, new_password]); + } + } + }, + + /** + * For now the VPN setup is not really streamlined + * + * src/leap/bitmask/vpn/README.rst for more info + */ + vpn: { + enable: function() { + return call(['vpn', 'enable']) + }, + + disable: function() { + return call(['vpn', 'disable']) + }, + + status: function() { + return call(['vpn', 'status']) + }, + + start: function(provider) { + return call(['vpn', 'start', provider]) + }, + + stop: function() { + return call(['vpn', 'stop']) + }, + + /** + * Check if the VPN is ready to start and has the cert downloaded + * + * @return {Promise<{'vpn_ready': bool, + * 'installed': bool}>} + */ + check: function(provider) { + if (typeof provider !== 'string') { + provider = ""; + } + return call(['vpn', 'check', provider]); + }, + + /** + * Download VPN cert + * + * @param {string} userid the userid to be used + */ + get_cert: function(userid) { + return call(['vpn', 'get_cert', userid]) + }, + + /** + * Install helpers in the system + */ + install: function() { + return call(['vpn', 'install']) + }, + + /** + * Uninstall helpers in the system + */ + uninstall: function() { + return call(['vpn', 'uninstall']) + }, + + /** + * List VPN gateways + * + * They will be sorted in the order that they will be used + * + * @return {Promise<{provider_name: [{'name': string, + * 'country_code': string, + * 'location': string, + * ...}]}> + */ + list: function() { + return call(['vpn', 'list']) + }, + + /** + * Get/set the location preference for the gateways + * + * @param {list} Order of preference of locations. + * If it's missing it will return the existing location list + */ + locations: function(locations) { + if (typeof locations !== 'list') { + locations = []; + } + return call(['vpn', 'locations'].concat(locations)) + }, + + /** + * Get/set the country preference for the gateways + * + * @param {list} Order of preference of countries. + * If it's missing it will return the existing country list + */ + countries: function(countries) { + if (typeof countries !== 'list') { + countries = []; + } + return call(['vpn', 'countries'].concat(countries)) + } + }, + + mail: { + enable: function() { + return call(['mail', 'enable']) + }, + + disable: function() { + return call(['mail', 'disable']) + }, + + /** + * Check the status of the email service + * + * @param {string} uid The uid to get status about + * + * @return {Promise} User readable status + */ + status: function(uid) { + return call(['mail', 'status', uid]); + }, + + /** + * Get the token of the active user. + * + * This token is used as password to authenticate in the IMAP and SMTP services. + * + * @param {string} uid The uid to get status about + * + * @return {Promise<{'token': string}>} The token + */ + get_token: function(uid) { + return call(['mail', 'get_token', uid]); + }, + + /** + * Get status on the mixnet for an address. + * + * @param {string} uid The uid to get status about + * @param {string} address The recipient address to be mixed + * + * @return {Promise<{'status': string}>} Where the status string can be 'ok', + * 'unsuported' or 'disabled' + */ + mixnet_status: function(uid, address) { + return call(['mail', 'mixnet_status', uid, address]); + } + }, + + /** + * A KeyObject have the following attributes: + * - address {string} the email address for wich this key is active + * - fingerprint {string} the fingerprint of the key + * - length {number} the size of the key bits + * - private {bool} if the key is private + * - uids {[string]} the uids in the key + * - key_data {string} the key content + * - validation {string} the validation level which this key was found + * - expiry_date {string} date when the key expires + * - refreshed_at {string} date of the last refresh of the key + * - audited_at {string} date of the last audit (unused for now) + * - sign_used {bool} if has being used to checking signatures + * - enc_used {bool} if has being used to encrypt + */ + keys: { + /** + * List all the keys in the keyring + * + * @param {string} uid The uid of the keyring. + * @param {boolean} priv Should list private keys? + * If it's not provided the public ones will be listed. + * + * @return {Promise<[KeyObject]>} List of keys in the keyring + */ + list: function(uid, priv) { + return call(['keys', 'list', uid, private_str(priv)]); + }, + + /** + * Export key + * + * @param {string} uid The uid of the keyring. + * @param {string} address The email address of the key + * @param {boolean} priv Should get the private key? + * If it's not provided the public one will be fetched. + * @param {boolean} fetch If the key is not in keymanager, should we fetch it remotely. + * If it's not provided keys will not be fetched remotely + * + * @return {Promise} The key + */ + exprt: function(uid, address, priv, fetch) { + var privstr = private_str(priv); + if ((typeof fetch === 'bool') && fetch) { + privstr = 'fetch'; + } + return call(['keys', 'export', uid, address, privstr]); + }, + + /** + * Fetch key by fingerprint + *, + * @param {string} uid The uid of the keyring. + * @param {string} address The email address of the key. + * @param {string} fingerprint The key fingerprnit. + * + * @return {Promise} The key + */ + fetch: function(uid, address, fingerprint) { + return call(['keys', 'fetch', address, fingerprint]); + }, + + /** + * Insert key + * + * @param {string} uid The uid of the keyring. + * @param {string} address The email address of the key + * @param {string} rawkey The key material + * @param {string} validation The validation level of the key + * If it's not provided 'Fingerprint' level will be used. + * + * @return {Promise} The key + */ + insert: function(uid, address, rawkey, validation) { + if (typeof validation !== 'string') { + validation = 'Fingerprint'; + } + return call(['keys', 'insert', uid, address, validation, rawkey]); + }, + + /** + * Delete a key + * + * @param {string} uid The uid of the keyring. + * @param {string} address The email address of the key + * @param {boolean} priv Should get the private key? + * If it's not provided the public one will be deleted. + * + * @return {Promise} The key + */ + del: function(uid, address, priv) { + return call(['keys', 'delete', uid, address, private_str(priv)]); + } + }, + + events: { + /** + * Register func for an event + * + * @param {string} event The event to register + * @param {string} name The unique name for the callback + * @param {function} func The function that will be called on each event. + * It has to be like: function(event, content) {} + * Where content will be a list of strings. + */ + register: function(event, name, func) { + event_handlers[event] = event_handlers[event] || {} + if (event_handlers[event][name]) { + return null; + } else { + event_handlers[event][name] = func; + return call(['events', 'register', event]) + } + }, + + /** + * Unregister from an event + * + * @param {string} event The event to unregister + * @param {string} name The unique name of the callback to remove + */ + unregister: function(event, name) { + event_handlers[event] = event_handlers[event] || {} + delete event_handlers[event][name] + if (Object.keys(event_handlers[event]).length == 0) { + return call(['events', 'unregister', event]); + } else { + return null; + } + } + } + }; +}(); + +try { + module.exports = bitmask +} catch(err) {} -- cgit v1.2.3