From cb977a27192d76a945fbffd07d1fb015e0dbc31e Mon Sep 17 00:00:00 2001 From: Kevin F Date: Wed, 3 Aug 2022 12:18:21 -0500 Subject: [PATCH] Removed more defunct code --- src/__init__.py | 8 - src/notifier/__init__.py | 55 ----- src/onionrprocess/__init__.py | 21 -- src/onionrsetup/setupconfig.py | 12 - src/onionrutils/dependencycheck.py | 1 - src/onionrutils/getclientapiserver.py | 37 ---- src/onionrutils/getopenport.py | 29 --- src/onionrutils/stringvalidators.py | 71 ++---- src/onionrutils/updater/__init__.py | 7 - src/onionrutils/validatemetadata.py | 127 ----------- src/utils/bettersleep.py | 30 --- src/utils/readoffset.py | 17 -- src/utils/reconstructhash.py | 51 ----- static-data/connect-check.txt | 1 - static-data/sounds/notification1.mp3 | Bin 15394 -> 0 bytes static-data/www/private/index.html | 19 -- static-data/www/shared/identicon.js | 205 ------------------ static-data/www/shared/images/favicon.ico | Bin 3568 -> 0 bytes static-data/www/shared/images/onionr-icon.png | Bin 5176 -> 0 bytes static-data/www/shared/images/onionr-text.png | Bin 6612 -> 0 bytes static-data/www/shared/useridenticons.js | 46 ---- 21 files changed, 21 insertions(+), 716 deletions(-) delete mode 100644 src/notifier/__init__.py delete mode 100644 src/onionrprocess/__init__.py delete mode 100644 src/onionrutils/dependencycheck.py delete mode 100644 src/onionrutils/getclientapiserver.py delete mode 100644 src/onionrutils/getopenport.py delete mode 100644 src/onionrutils/updater/__init__.py delete mode 100644 src/onionrutils/validatemetadata.py delete mode 100644 src/utils/bettersleep.py delete mode 100644 src/utils/readoffset.py delete mode 100644 src/utils/reconstructhash.py delete mode 100755 static-data/connect-check.txt delete mode 100644 static-data/sounds/notification1.mp3 delete mode 100755 static-data/www/private/index.html delete mode 100644 static-data/www/shared/identicon.js delete mode 100644 static-data/www/shared/images/favicon.ico delete mode 100755 static-data/www/shared/images/onionr-icon.png delete mode 100644 static-data/www/shared/images/onionr-text.png delete mode 100644 static-data/www/shared/useridenticons.js diff --git a/src/__init__.py b/src/__init__.py index 32cf8455..85faccf5 100755 --- a/src/__init__.py +++ b/src/__init__.py @@ -46,14 +46,6 @@ locale.setlocale(locale.LC_ALL, '') # noqa ran_as_script = False if __name__ == "__main__": ran_as_script = True -# Import standard libraries - -try: - from onionrutils import dependencycheck # noqa -except ModuleNotFoundError as e: - print('Missing requirement: ' + str(e) + ' installed') - sys.exit(1) - # Import 3rd party libraries from filenuke import nuke # noqa diff --git a/src/notifier/__init__.py b/src/notifier/__init__.py deleted file mode 100644 index 9acf6f54..00000000 --- a/src/notifier/__init__.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Onionr - Private P2P Communication. - -Desktop notification wrapper -""" -from subprocess import Popen - -try: - import simplenotifications as simplenotify -except ImportError: - notifications_enabled = False -else: - notifications_enabled = True - -from utils.readstatic import get_static_dir -import config -from onionrplugins.onionrevents import event as plugin_api_event -""" -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 . -""" - -if not config.get('general.show_notifications', True): - notifications_enabled = False - -notification_sound_file = get_static_dir() + "sounds/notification1.mp3" - - -def notify(title: str = "Onionr", message: str = ""): - """Cross platform method to show a notification.""" - if not notifications_enabled: - return - plugin_api_event("notification", data={"title": title, "message": message}) - simplenotify.notify(title, message) - - -def notification_with_sound(sound='', **kwargs): - if not notifications_enabled: - return - if not sound: - sound = notification_sound_file - try: - Popen(["mpv", sound]) - except FileNotFoundError: - pass - notify(**kwargs) diff --git a/src/onionrprocess/__init__.py b/src/onionrprocess/__init__.py deleted file mode 100644 index ffd26feb..00000000 --- a/src/onionrprocess/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -from audioop import mul -import multiprocessing - - -def run_func_in_new_process(func, *args, **kwargs): - queue = multiprocessing.Queue() - - def _wrap_func(): - if args and kwargs: - queue.put(func(*args, **kwargs)) - elif args: - queue.put(func(*args)) - elif kwargs: - queue.put(func(**kwargs)) - else: - queue.put(func()) - - proc = multiprocessing.Process(target=_wrap_func, daemon=True) - proc.start() - return queue.get() - diff --git a/src/onionrsetup/setupconfig.py b/src/onionrsetup/setupconfig.py index 6a502831..f0013a99 100755 --- a/src/onionrsetup/setupconfig.py +++ b/src/onionrsetup/setupconfig.py @@ -10,7 +10,6 @@ import ujson as json import config import logger import onionrvalues -from onionrutils import getopenport from logger.settings import * from utils import readstatic """ @@ -77,14 +76,3 @@ def setup_config(): set_level(map[verbosity]) else: logger.warn('Verbosity level %s is not valid, using default verbosity.' % verbosity) - - if type(config.get('client.webpassword')) is type(None): - config.set('client.webpassword', base64.b16encode(os.urandom(32)).decode('utf-8'), savefile=True) - if type(config.get('client.client.port')) is type(None): - randomPort = getopenport.get_open_port() - config.set('client.client.port', randomPort, savefile=True) - if type(config.get('client.public.port')) is type(None): - randomPort = getopenport.get_open_port() - config.set('client.public.port', randomPort, savefile=True) - if type(config.get('client.api_version')) is type(None): - config.set('client.api_version', onionrvalues.API_VERSION, savefile=True) \ No newline at end of file diff --git a/src/onionrutils/dependencycheck.py b/src/onionrutils/dependencycheck.py deleted file mode 100644 index 6b83ebc2..00000000 --- a/src/onionrutils/dependencycheck.py +++ /dev/null @@ -1 +0,0 @@ -from urllib3.contrib.socks import SOCKSProxyManager # noqa \ No newline at end of file diff --git a/src/onionrutils/getclientapiserver.py b/src/onionrutils/getclientapiserver.py deleted file mode 100644 index 1d196eae..00000000 --- a/src/onionrutils/getclientapiserver.py +++ /dev/null @@ -1,37 +0,0 @@ -''' - Onionr - Private P2P Communication - - Return the client api server address and port, which is usually random -''' -''' - 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 . -''' -import filepaths -import config -def get_client_API_server(): - config.reload() - retData = '' - getconf = lambda: config.get('client.client.port') - port = getconf() - if port is None: - config.reload() - port = getconf() - try: - with open(filepaths.private_API_host_file, 'r') as host: - hostname = host.read() - except FileNotFoundError: - raise FileNotFoundError - else: - retData += '%s:%s' % (hostname, port) - return retData diff --git a/src/onionrutils/getopenport.py b/src/onionrutils/getopenport.py deleted file mode 100644 index 97d64ff7..00000000 --- a/src/onionrutils/getopenport.py +++ /dev/null @@ -1,29 +0,0 @@ -''' - Onionr - Private P2P Communication - - get an open port -''' -''' - 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 . -''' -import socket -def get_open_port(): - # taken from (but modified) https://stackoverflow.com/a/2838309 by https://stackoverflow.com/users/133374/albert ccy-by-sa-3 https://creativecommons.org/licenses/by-sa/3.0/ - # changes from source: import moved to top of file, bind specifically to localhost - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind(("127.0.0.1",0)) - s.listen(1) - port = s.getsockname()[1] - s.close() - return port \ No newline at end of file diff --git a/src/onionrutils/stringvalidators.py b/src/onionrutils/stringvalidators.py index 3655c04e..8dbc3173 100644 --- a/src/onionrutils/stringvalidators.py +++ b/src/onionrutils/stringvalidators.py @@ -1,49 +1,30 @@ -''' - Onionr - Private P2P Communication +""" +Onionr - Private P2P Communication - validate various string data types -''' -''' - 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 . -''' -import base64, string +validate various string data types +""" +import base64 +import string import unpaddedbase32, nacl.signing, nacl.encoding from onionrutils import bytesconverter -def validate_hash(data, length=64): - ''' - Validate if a string is a valid hash hex digest (does not compare, just checks length and charset) +""" +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. - Length is only invalid if its *more* than the specified - ''' - retVal = True - if data == False or data == True: - return False - data = data.strip() - if len(data) > length: - retVal = False - else: - try: - int(data, 16) - except ValueError: - retVal = False +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 . +""" - return retVal def validate_pub_key(key): - ''' - Validate if a string is a valid base32 encoded Ed25519 key - ''' + """Validate if a string is a valid base32 encoded Ed25519 key""" if type(key) is type(None): return False # Accept keys that have no = padding @@ -54,18 +35,8 @@ def validate_pub_key(key): nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder) except nacl.exceptions.ValueError: pass - except base64.binascii.Error as err: + except base64.binascii.Error as _: pass else: retVal = True return retVal - - -def is_integer_string(data): - '''Check if a string is a valid base10 integer (also returns true if already an int)''' - try: - int(data) - except (ValueError, TypeError) as e: - return False - else: - return True diff --git a/src/onionrutils/updater/__init__.py b/src/onionrutils/updater/__init__.py deleted file mode 100644 index a9bdf13f..00000000 --- a/src/onionrutils/updater/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -import notifier - - -def update_event(bl): - """Show update notification if available, return bool of if update happened""" - if not bl.isSigner(onionrvalues.UPDATE_SIGN_KEY): raise onionrexceptions.InvalidUpdate - onionr.notifier.notify(message="A new Onionr update is available. Stay updated to remain secure.") diff --git a/src/onionrutils/validatemetadata.py b/src/onionrutils/validatemetadata.py deleted file mode 100644 index 9616332d..00000000 --- a/src/onionrutils/validatemetadata.py +++ /dev/null @@ -1,127 +0,0 @@ -"""Onionr - Private P2P Communication. - -validate new block's metadata -""" -from json import JSONDecodeError -import ujson as json - -import logger, onionrexceptions -import onionrvalues -from . import stringvalidators, epoch, bytesconverter -import config, filepaths, onionrcrypto -""" -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 . -""" - - -def validate_metadata(metadata, block_data) -> bool: - """Validate metadata meets onionr spec (does not validate proof value computation), take in either dictionary or json string""" - - ret_data = False - max_clock_difference = onionrvalues.MAX_BLOCK_CLOCK_SKEW - - # convert to dict if it is json string - if type(metadata) is str: - try: - metadata = json.loads(metadata) - except JSONDecodeError: - pass - - # Validate metadata dict for invalid keys to sizes that are too large - maxAge = onionrvalues.DEFAULT_EXPIRE - if type(metadata) is dict: - for i in metadata: - try: - onionrvalues.BLOCK_METADATA_LENGTHS[i] - except KeyError: - logger.warn('Block has invalid metadata key ' + i) - break - else: - testData = metadata[i] - try: - testData = len(testData) - except (TypeError, AttributeError) as e: - testData = len(str(testData)) - if onionrvalues.BLOCK_METADATA_LENGTHS[i] < testData: - logger.warn('Block metadata key ' + i + ' exceeded maximum size') - break - if i == 'time': - if not stringvalidators.is_integer_string(metadata[i]): - logger.warn('Block metadata time stamp is not integer string or int') - break - isFuture = (metadata[i] - epoch.get_epoch()) - if isFuture > max_clock_difference: - logger.warn('Block timestamp is skewed to the future over the max %s: %s', (max_clock_difference, isFuture)) - break - if (epoch.get_epoch() - metadata[i]) > maxAge: - logger.warn('Block is outdated: %s' % (metadata[i],)) - break - elif i == 'expire': - try: - if not int(metadata[i]) > epoch.get_epoch(): raise ValueError - except ValueError: - logger.warn('Block is expired: %s less than %s' % (metadata[i], epoch.get_epoch())) - break - elif i == 'encryptType': - try: - if not metadata[i] in ('asym', 'sym', ''): raise ValueError - except ValueError: - logger.warn('Invalid encryption mode') - break - elif i == 'sig': - try: - metadata['encryptType'] - except KeyError: - signer = metadata['signer'] - sig = metadata['sig'] - encodedMeta = bytesconverter.str_to_bytes(metadata['meta']) - encodedBlock = bytesconverter.str_to_bytes(block_data) - if not onionrcrypto.signing.ed_verify(encodedMeta + encodedBlock[1:], signer, sig): - logger.warn(f'Block was signed by {signer}, but signature failed') - break - else: - # if metadata loop gets no errors, it does not break, therefore metadata is valid - # make sure we do not have another block with the same data content (prevent data duplication and replay attacks) - - # Make sure time is set (validity was checked above if it is) - if not config.get('general.store_plaintext_blocks', True): - try: - if not metadata['encryptType']: - raise onionrexceptions.PlaintextNotSupported - except KeyError: - raise onionrexceptions.PlaintextNotSupported - try: - metadata['time'] - except KeyError: - logger.warn("Time header not set") - return False - - nonce = bytesconverter.bytes_to_str(onionrcrypto.hashers.sha3_hash(block_data)) - try: - with open(filepaths.data_nonce_file, 'r') as nonceFile: - if nonce in nonceFile.read(): - # we've seen that nonce before, so we can't pass metadata - raise onionrexceptions.DataExists - except FileNotFoundError: - ret_data = True - except onionrexceptions.DataExists: - # do not set ret_data to True, because data has been seen before - logger.warn(f'{nonce} seen before') - raise onionrexceptions.DataExists - else: - ret_data = True - else: - logger.warn('In call to utils.validateMetadata, metadata must be JSON string or a dictionary object') - - return ret_data diff --git a/src/utils/bettersleep.py b/src/utils/bettersleep.py deleted file mode 100644 index a3b5b8cc..00000000 --- a/src/utils/bettersleep.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Onionr - Private P2P Communication. - -greenlet safe sleep, ignoring ctrl-c -""" -from gevent import sleep -from onionrutils.epoch import get_epoch -""" -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 . -""" - - -def better_sleep(wait: int): - """Sleep catching ctrl c for wait seconds.""" - start = get_epoch() - try: - sleep(wait) - except KeyboardInterrupt: - better_sleep(wait - (get_epoch() - start)) - diff --git a/src/utils/readoffset.py b/src/utils/readoffset.py deleted file mode 100644 index ecfcba4f..00000000 --- a/src/utils/readoffset.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Onionr - Private P2P Communication. - -read from a file from an offset (efficiently) -""" -from collections import namedtuple - -OffsetReadResult = namedtuple('OffsetReadResult', ['data', 'new_offset']) - - -def read_from_offset(file_path, offset=0): - with open(file_path, 'rb') as f: - if offset: - f.seek(offset) - data = f.read() - offset = f.tell() - - return OffsetReadResult(data, offset) diff --git a/src/utils/reconstructhash.py b/src/utils/reconstructhash.py deleted file mode 100644 index 435d25ec..00000000 --- a/src/utils/reconstructhash.py +++ /dev/null @@ -1,51 +0,0 @@ -''' - Onionr - Private P2P Communication - - z-fill (zero fill) a string to a specific length - intended for reconstructing block hashes -''' -from typing import Union -''' - 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 . -''' - - -def reconstruct_hash(hex_hash: Union[str, bytes], - length: int = 64) -> Union[str, bytes]: - """Pad hash hex string with zeros, return result""" - return hex_hash.zfill(length) - - -def deconstruct_hash(hex_hash: Union[str, bytes]) -> Union[str, bytes]: - """Remove leading zeros from hex hash, return result""" - new_hash = '' - ret_bytes = False - try: - hex_hash = hex_hash.decode() - ret_bytes = True - except AttributeError: - pass - - c = 0 - for x in hex_hash: - if x == '0': - c += 1 - else: - break - new_hash = hex_hash[c:] - - if ret_bytes: - - new_hash = new_hash.encode() - return new_hash diff --git a/static-data/connect-check.txt b/static-data/connect-check.txt deleted file mode 100755 index ef09c785..00000000 --- a/static-data/connect-check.txt +++ /dev/null @@ -1 +0,0 @@ -https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/robots.txt,http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/robots.txt,http://rurcblzhmdk22kttfkel2zduhyu3r6to7knyc7wiorzrx5gw4c3lftad.onion/ \ No newline at end of file diff --git a/static-data/sounds/notification1.mp3 b/static-data/sounds/notification1.mp3 deleted file mode 100644 index 8021af7314ef89cd2aba767f7bcc34ba18b1c1d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15394 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 diff --git a/static-data/www/private/index.html b/static-data/www/private/index.html deleted file mode 100755 index ffaca192..00000000 --- a/static-data/www/private/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - Onionr - - - - - - -

Onionr

- - - - \ No newline at end of file diff --git a/static-data/www/shared/identicon.js b/static-data/www/shared/identicon.js deleted file mode 100644 index cd351cce..00000000 --- a/static-data/www/shared/identicon.js +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Identicon.js 2.3.3 - * http://github.com/stewartlord/identicon.js - * - * PNGLib required for PNG output - * http://www.xarg.org/download/pnglib.js - * - * Copyright 2018, Stewart Lord - * Released under the BSD license - * http://www.opensource.org/licenses/bsd-license.php - */ - -(function() { - var PNGlib; - if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { - PNGlib = require('./pnglib'); - } else { - PNGlib = window.PNGlib; - } - - var Identicon = function(hash, options){ - if (typeof(hash) !== 'string' || hash.length < 15) { - throw 'A hash of at least 15 characters is required.'; - } - - this.defaults = { - background: [240, 240, 240, 255], - margin: 0.08, - size: 64, - saturation: 0.7, - brightness: 0.5, - format: 'png' - }; - - this.options = typeof(options) === 'object' ? options : this.defaults; - - // backward compatibility with old constructor (hash, size, margin) - if (typeof(arguments[1]) === 'number') { this.options.size = arguments[1]; } - if (arguments[2]) { this.options.margin = arguments[2]; } - - this.hash = hash - this.background = this.options.background || this.defaults.background; - this.size = this.options.size || this.defaults.size; - this.format = this.options.format || this.defaults.format; - this.margin = this.options.margin !== undefined ? this.options.margin : this.defaults.margin; - - // foreground defaults to last 7 chars as hue at 70% saturation, 50% brightness - var hue = parseInt(this.hash.substr(-7), 16) / 0xfffffff; - var saturation = this.options.saturation || this.defaults.saturation; - var brightness = this.options.brightness || this.defaults.brightness; - this.foreground = this.options.foreground || this.hsl2rgb(hue, saturation, brightness); - }; - - Identicon.prototype = { - background: null, - foreground: null, - hash: null, - margin: null, - size: null, - format: null, - - image: function(){ - return this.isSvg() - ? new Svg(this.size, this.foreground, this.background) - : new PNGlib(this.size, this.size, 256); - }, - - render: function(){ - var image = this.image(), - size = this.size, - baseMargin = Math.floor(size * this.margin), - cell = Math.floor((size - (baseMargin * 2)) / 5), - margin = Math.floor((size - cell * 5) / 2), - bg = image.color.apply(image, this.background), - fg = image.color.apply(image, this.foreground); - - // the first 15 characters of the hash control the pixels (even/odd) - // they are drawn down the middle first, then mirrored outwards - var i, color; - for (i = 0; i < 15; i++) { - color = parseInt(this.hash.charAt(i), 16) % 2 ? bg : fg; - if (i < 5) { - this.rectangle(2 * cell + margin, i * cell + margin, cell, cell, color, image); - } else if (i < 10) { - this.rectangle(1 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image); - this.rectangle(3 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image); - } else if (i < 15) { - this.rectangle(0 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image); - this.rectangle(4 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image); - } - } - - return image; - }, - - rectangle: function(x, y, w, h, color, image){ - if (this.isSvg()) { - image.rectangles.push({x: x, y: y, w: w, h: h, color: color}); - } else { - var i, j; - for (i = x; i < x + w; i++) { - for (j = y; j < y + h; j++) { - image.buffer[image.index(i, j)] = color; - } - } - } - }, - - // adapted from: https://gist.github.com/aemkei/1325937 - hsl2rgb: function(h, s, b){ - h *= 6; - s = [ - b += s *= b < .5 ? b : 1 - b, - b - h % 1 * s * 2, - b -= s *= 2, - b, - b + h % 1 * s, - b + s - ]; - - return[ - s[ ~~h % 6 ] * 255, // red - s[ (h|16) % 6 ] * 255, // green - s[ (h|8) % 6 ] * 255 // blue - ]; - }, - - toString: function(raw){ - // backward compatibility with old toString, default to base64 - if (raw) { - return this.render().getDump(); - } else { - return this.render().getBase64(); - } - }, - - isSvg: function(){ - return this.format.match(/svg/i) - } - }; - - var Svg = function(size, foreground, background){ - this.size = size; - this.foreground = this.color.apply(this, foreground); - this.background = this.color.apply(this, background); - this.rectangles = []; - }; - - Svg.prototype = { - size: null, - foreground: null, - background: null, - rectangles: null, - - color: function(r, g, b, a){ - var values = [r, g, b].map(Math.round); - values.push((a >= 0) && (a <= 255) ? a/255 : 1); - return 'rgba(' + values.join(',') + ')'; - }, - - getDump: function(){ - var i, - xml, - rect, - fg = this.foreground, - bg = this.background, - stroke = this.size * 0.005; - - xml = "" - + ""; - - for (i = 0; i < this.rectangles.length; i++) { - rect = this.rectangles[i]; - if (rect.color == bg) continue; - xml += ""; - } - xml += "" - - return xml; - }, - - getBase64: function(){ - if ('function' === typeof btoa) { - return btoa(this.getDump()); - } else if (Buffer) { - return new Buffer(this.getDump(), 'binary').toString('base64'); - } else { - throw 'Cannot generate base64 output'; - } - } - }; - - if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { - module.exports = Identicon; - } else { - window.Identicon = Identicon; - } -})(); diff --git a/static-data/www/shared/images/favicon.ico b/static-data/www/shared/images/favicon.ico deleted file mode 100644 index 3c11d7e278a51daabb68f484a727c6d564bbbbfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3568 zcmVokdQ-k7BGR(AkST783b2hV2?xuSph+igYINTd5r@cU|0-} z3nRD;E3&-i=qxZJXM!#vhec--Kp-K61w_aJBMIc$q+j=0cmFVf?xaCXH$8i1=KHIw zepU7ReqYzETeltr4}i1t-ybbkH@c0WI#_VpXX-u~rfxh))wqH19C(KW&Ewz8S;aF_ zdU9FrzrXzXchYvUa9p=)I9#BAY>>EMyVu#$=bQtQjaaDBn)7(25^+YG`|p> z_5w=}XW_v2UeY6#k0`+L3F|WmPd_w*>PP@8?~sbAB5IL(@YU*tIh{MajZePBG0G0W>rnXd8zYWGRd52UiCMPT%v~yuuT-e7L z<+HcC)kbfD?vuxX(rby?>5FrIHEVxpnJ@t=^LI`4p0D0#>da@$O5HN(g9XduOZnlT zeD1dOfvdM(B|LQ`WT)rlY`QDL9RZF{Sf9ak<1-j61;DeTj;$>EldyeAC>gV6Jb(k> zXanWBC)yqd3Vb`@5uSbs#xiYLsaxU^{EF)J~7TNqG8U z#iAWN&?zQZ-)sehIDvn3DU+~p}K1Iz%{|#X6!}b-5K_4tw&O4-HBdCs4YCRKy zRsl4Re<=V}OioxD45L)eLq1!0W#QO(aG;W~4+f)hb)%aaL3J>d1W0MFxcvK7!KyI=5mMUHDMCsf zPxy_pDUZIP-k1{)4pN${j7kEGpgNcePWwzIYv+0N!D*jKb)Os$pLdWYWraQSDpTFMsXGCnMQ`a7PcFJo1|RRQx@Nuk zvCy=EyZ*h*8&@u-ZhZS1j3;iWH|}fSUAKE&oTvam`~`4w&hGcKR|Tu?YmYVfcsV-$ z)OrU~^;PfHo*L)r=DBCNdaXWQlmNuB2S`0#&w-h5{L_|fWL|egEMpA{7`w@}?)Zd% ziOZY>h@$|eVYmPw(I;W{heMIzwS?J4g~NiKBhmf5kiiuF2l3(+zaEg*rKat7kGe!W z_i-)=`!>z#1k-=#M!&C8<3;BV;wFISi>pUNmtWi!>Bv3b69Djdc?O1^?tPb(B*cqW z`~+|g>Eol4c&#nXGm2;bFc3X%CgL9t4FG4}r6MHRF2svh+yv-Jx&SrNE$Ya&y$2lHhDET}%kg6dPc zSN5Eo^2?7bDT(WHx6l9PVQ_qCkHE=E8760B~xcxvj# zc`>F4;34#Y)6?E{f#8U8({skqS6J-TdiO{8`^gP00%V&Vp;+UPxE>PCm)iNG~bk>Bm;o3_vJE9VCO zdS0M?ne$72irY?u9_N?v4NC~Sz`h? zH|p5RfzdQ{dd6m#+Ti`vRP~`@4tT0^R?7w<*}gStaG$2SL*-6t;ivM6wmjYAcj{gl zywe8Ca~A-dVY;z5@v-!}rjjpRLb79(*65oDKniV>O2S?yYjZ{&TN!b~^FdHDW=#cv z0Wli`v?kxW_Q8XYs)X$H7l!>|@e185I~$759b~FrtKSZ8ma7{_>t1;PfC0KEFhFha zKCd=--!^=5ZOggq2NH98XAC{?)7J=3CsUCRTef1dHdAogHpYT?Oav&Kz4aBQ>aT?6 z5uRSMq$d5H{gLiRPdrmlpf&sdSNF=}!OZ)%W$FyNSDw;x_Ik;w=Qgb#S^V>&gq-wA z)FSvo%W0m#yi<9b#z)INY5^+qcRA#mrZ+WmGU^9`#?LPvG5pGs?+QEu zB(=U}Wy#;K+3fqW_87Ga&jJV)LDE|SQTKwV1yE`|^Gwy~6`IFc__=C>_k8`QXIH3o zEuG)^Fael&mGJbPF2f%^D!A-hLd{Ik`=6e%d1|NIM+^ke{mSC725n*)mBjr*lI?jT zp#C&c9U}4`KFPKnzy!k62>>Q=cFT5ZiTqMByz)N>uH2kB^mnf>CmgK`^WW@$IU4>^ z2~e58E6wo9X`y*y7yCv4qT1+1VG(B)Vd@N=&4Q#snLlw3$;vW; z{q5_R21DRl{ZarRJXa8AV{H7H8Jj!3ju@2yfvUQ&?neX;&f$;l*S$eEleG?O{6ey$ z*fqNEWX>ke4}Q~h!w9N}0US00%HifOu3nUDo5vI21tM_&)xNID%%SCTem&aJFYO%R z=$+ReQ*%lKLZCX4kcZ=fdCd%%plt;Mtr_vrcdcQrLHasTut{8$(nAXwCkxwAd}t z0|1=O(s5wo=*N#0%($$30&8L3V+Lw4D7Btdwye}LK0)fRD55s{TmYbZ0%4B{@3dug zHip=K44LL1fsVoe0H0{B`Et#Hk*2}YIv7J>1eENC;!3C1?BCPgMkOKQvrd3c?I=6N&qe)+_7ByZ3i%Qb~Hlq7f(O` zZkmwn*Z^{m1FD2TblKn0ymE#SR6C69)GAg10G}X*?H?L`xwumSAX)*eV%QoV75SL} zz$Zu@7d4rgsNA}}dhp&?S6O?x-U20Ib9+#7jk8R4>g$?c9M+<{0S+oOQ+WhqaFnLBwLvJhF=-#yHqp9(k*d66(j#M zOwrN|zw)!-^@ef)wZ4lrhfK{_%M{HKsl2s!O5yNNmX-Ri-dX}?h6yC4Ie*`8-Ly_G zj6^L!LjRuIs3f$6=IJfL_Y6hP9Z~eNyhwBV?*k#(Q6xBR(@#y^n5%jGt3z#^O{^L8 z!7Cf0X+LTK`u$>B4S*kq?)#Yrb83y=10`eDI0*0ppJdw!;CmfLA{^DlZi)YDO;4U2 ztZwY1dHe^Ntc4cQEj_xdh?YMkJQr;nu=a9&1dxwH^NfJni(2>!OZTMgVF%xsW6emO z&e<(zLB2x>N>t8n`NYya>50exGyioaYftN*z^A6pGQxbOCl?L+V8M=PcoUuj<)fHH zv>|u<0vV<`i?dtWy%qF6%FWwH%4YrB`lQFRjtxJwbdZ>w@Hm$sEeEidPqdbbsfiGr z_A(H%S4wxSvG?gVV)&t@&u~_;?Bt{kEB#k*9WagX;He}OQwu*WTHd@v0x21@W)GEw znU20a2S$5VurhyFAOGdrKN z3}<^g;b;-Hij#*IF6s37zRLXF1ALcmR4`fF%X3!oj1g34$~8@H08t5$vuRe<#Z~)$ z*j##fy{Q>fLT!d$kqp1G5|k`E`J=a+n5KWif?5+O0hkHF<>+q&Rkzycb;3lXZmIK5 zTcN45>Cpra0IgSQz*)sJyp*0?=D&K2FZ$@0sv3O!Vv)EZeLIa;K|DTU2gxCn|Y?rCNf3K#(lIPJO`7p z&k3amY?&$hbGI*u**+iuw8kh3PJ02+dS|F&(GEU%t^PA3pp3NjNqy^K-5R<4)JeTmPpb8*8-2a)9vEk(iUdIP?PLUoTEZ qs`wXub+!$E(U+&&PbXFRCodHU3rvM#hI_ScK7S17X$=E+0+DN-^DqT%w#p5<&XLU2ip8Y2@u`6sS1!7*`(d)zh4WIGeHkdrwx0TCK#Xh0g2O$3##yLtV(`)xJf z@4|i7ty}lLy6@fB?{cP#!?|_ut@^6!_rCkp_kHzMHDiNm5P?Ai1`#-Y5V)%2Dt`L( z*51>Q{ZTNUENEN6llde|gcEGtjCFzSryE*fx0ls=ciW!Zcu&5^Xg-xsvE{p#pOzdP zNO0FD*E3H4%+#K0H?ybJHIU$T<+>~&np|vocpBUU4&0k}-^?Kj#FXPXwshyx4_H}z z8Q^^2!0qVmu!sa;2N7{7eJH>%s=F3Aa1W*rDtmf7cU<{A$PWVtZknf+cqMa5cK6$N zTSUW(JuCP}w|tZ@*}25>TuGn`4+916vR%s@L4s)5*SF6C(W}Q_Wk<3{SZlJ?@;oRv zfdcov^n0$j?CRZBc)#e4MKsOjjQc#xv&(l}?)bwLDh+TZP~e`(pKu-i(d^N}`~Cg< z3wdOMIN=2fT*y_olx11t_2Q1jhG{I8?ap(7XVT@ ztHZ}j-Aa@V47h%{75n(0SWq%aPwwQ(gYu>3L#SgxN`c{AKjbC!N)A1M&v-raFWxms8`ze&aFR@MAnzvw@Bo_et6H*c|Fv+CKAp){$L z8KL0hY_AxCE`+Pbxf{Ro2v5J?%`ykO*z{E`?6P0YWM{tL06Vs{gnIPy)M!2Q*|9W> z)Wz9?|GKrzZ=rOcy$+%DmZX~b^vP8O^KqEaobtvf_Obstk2QAJdgfpN`yvSI$zLWJbrZdD`WC9j ztd?PEj=)mKmzz-74gf$&^u-PCoz!tA3+L=*2mH`-L^hyl3o{N2M14jz)M zQ3Fhi7SpzlW*4uT%$mFF0>!cBz^VprBtf9Lq-ZX}rfwh2CUuNpA*fkqLZ1E)KUz=V zpKoXgK@tUKH*u{RxY2q7H{>o;8*&++T(V|(Bg-6hkFo*LNIpc9+b9MDVbabK z4Az`Zn)$@Cs!CnGUh0gbgfejgS0~}~hZvaAeip0k*W>mu`4y_2stWF;?=K25g{7tf z1pq@cC?;pky0s&l&1}`KRRtHLjwC9;Vf2H|e0q6RMFPfFeQ+Dd1Lkv5`7L~ILW^8g zf89-AeU$g^JHmLz>VDMfd67bpxix1tu~|R6rfOpx@P^yI{y6X1Njq~yleP#@Wxpm% zu=cDLHv9SO2Xwpx)>!LhKj!HpCo00F+TcgRtY+8{Hs_Ta24vh7Z)E+V2MAP(`lgkE zAf?#Q1~%_MZ>s3{D@J_%mkHE&t=mk41g_baXk#6l|GQf%f`jiwH(&P@Pwt?Ndb#3J z;^qLWekIkde&Pr=Jku#=TvzYHM}YpEwuakFoj_(Ev=?XU3T zfA8>Xm{vM8(}&~aZj^k#`7}i|Rzi*w7dK8lhfRIpsuo50qapW|I^`}`3D5daw@ zZ&m}V9o51vxbKRh{oA(UMI*SRpX7V4p8}|JK1`ehaP8Sc+3aVoE84!56RdY-Ro-~j z!@T!x+IUwg2T|+cc$CFQG_l#wl{XM^Seusol=mI%Wcf60y(+bDM6hO5Gn@0$H%rE- z|%wq(mDNh7V!#o&TyembI7M%X29TbMn@G zlEG(q7F}8$G8tj>HBa#58|uR*%rf#7Ak>~Ul+AkPTGzHVEj=YC`)E?~IB>JgaBa-^ zGUmMOE+@C#@C;9GJ3ykso5Lt^?wM@n6Z*Qa>wwKpo7dI<{C)1_vkJu`ta;)XPOO`dL_=rr#$4t6!&n4Ge^Jnsk?Z--@_&q@=dg0Jepsh;uK&{gJbl>x ztOv2F*ib!7PHQE?3$Sp6H~|YTfpfeX3Ea8%v-c^b`P^UJ7}8xVMc*@>DfY%~FDq$@ zV^eaMSiqQFHG_ylG;3PmF<0VmC)U$c$fi|E7TdEp=b-9|$k zp6ko4-+rEVweA+nJLa8$-F6Gn*tnPRfO*LpCXZp$A6{Y`|7#cD!}Gm*`;fuybARzI z8x0dASFvEY4$YEoSHszHwyngrTVLQ^oAw0`SadYmagc4f{%PB%ec`JsNQrjD%QD9F zVfUk79KmhB=~TOi6A>Y81A1$`^sH=64XI;|L`%2!>mvn-3-S!)wu>qVIKU}+I z>`HPf>{)_i(%PqG7K#s%gY??iuUG(4I3z`3$=T2a-mN2Q5oe1EypZ$T2`{qM!vwe&9 z)^e#d+j`S;w)!Zktp57vf5@|>ZWK$Z^`|Y@KgIF6m1V+`bv5UOZz%hw>QmcSALG66 z9Ao)1OlNrLWDR2N=kDV<4JL!%#E{ie=af;7*%NwPiw?=j@I5GhN4cbQPf#W%knR4Y zzU^*Om~kqaar3oL@}6DqmSpv%-m-i7;AlGbzdOuk9IsB=g^fG5edh(H3qU#Cx6ciu2t&QQ>b zobYvalb&8Isr(Z*&V_K#ohYt?VR8{0Y`^hY-o2@KC&W5%M=o4-yM>PVe)Ba?^3>kL z%;}RuIZSwUV~4SskE?^6b;n}1iEHf`T04`%o^-zi855hfpk<;eHj4C+QtmZ1kV{aN z*8R&$@*BV73M}fOuPKtTYd>?3Qpzv*{dcqt0<$0KQyad`dtC9Ql#(pqZoBnGZY)_B zp%iOM*h)Yp@sgrMW7>mu^K;F|@8mk6{9;>Zd(Fr1;M-TcWCIy~T`x)H->n%=J2^Gd zrlpTs+P8q4gH3IDt)>`nv37XNH-Dy4OQ1#O^8DZ0U6O0adF^#|z@&-=$j%LRKL{o$ zIBT>MS#F!JeabSP_%OZg1qOaXLl$s#9oID@t&>^$eP=60XseKoDgvIdOwYZn3KXb) zkU{@n`?U4wZM=;Qvoga6HPkutEC}c+Bvn6gBn3z)=S{I#A+OT&b)wbfdKPfCKvHy4C8r8KRb-|=wA3=v zjaQTNL;BSf)wCa_c3V#)g~}G9^)KBdf8Itt4NsXoUs+nz5epVn%-)s>j7mo|Ntbd} zffJ^+FYTm?ZO{N$c2rrw)oNVPh~*nBMnutKzQ6d1&&9}!?|H_);^B6_gIL!ouNbyd z(ZX}ZPz&z{Gi4&+3%E*A;nXn|n=9nFQ+2K93VJY|2Rd26)tM2w7*=HV*2_1e7-O{F z-@Rxm5dbZsoTT;RNH|!)RdveNAq6-Hl<<c@`05)a%HRH&zmqSbzaPcCdv7(ymvAY-R&ar=vVncEsCNq{$$!5?l7HP4%9#Mxr@HfU3f1bhStPmPeAZ=5;Sh}Tm2g|S%K ze@Md-B{iNNd`u{s?)jEacTxv0x}CgktK(SrzIM z-}cgM{@TTMKWw3lhYVtEPaZj-{2-hu(U?`sPR5Q<=URm~*qR_G8@MQdg}jdEG>KHr;RYd&!&Z~NL~Hkx8Nr#y67)c5-3nDkW3P8*49wYC$GWj6g`s~Z(` zl_)~)Ah@DfGWIe^h2~R4`Rb3+0q-TaA>r~wyI5_7I)q8mSOt*U5O1+=)&FiMJJJ5O z_}E*Xz(K#|N_|9EC*hzBRk9H@3$3OFKc->TsDgk`Zdu*y&sI@%h-6_%wA+cdOI>R- z$0uIv&l^cQ)~(C)h+gd7qhWl|1bfk6)!_k3v4Do-bl8JVfcW4ITbz_MPd$XzAp?t7y0TWa9%#1b2=3gUQ^d3*Uz^oWUX$DHiu&wluM4Sk zMr+OL-ob}p#5PFXK(vm|stl^tpXcm!as(F@b-54vDgrrBM@%?@!|TecSih2aOkk?W zMC^;x4TFL=aLNzuLEmP-UBNZNdNj;o5JMn`N_&|1%bP;bor9DxPpIN1+R2B2$x$oO zS8X_(Tnf8_i+YHi(oK+4n<+k5E|1fY3~!aDn33!Ic2Dgi8d0?i2j@1N*t}0GC z^LE-(*17gg?Ts<2@LAO`Yz?Uj;TaTkT_F$GTVlcp$4$tsSI}7vu=@vMU5iE-A3mI9X%AYR(?T_6);NZMRu$y1BV}Rnp^J$t7L)TO zfeRvV5yF#IN`eBf(lbk;rBlc2mVc3^)1GFrj8nd3a6zyffDKNj$pQNB)ceanAWa_N zR@#-+xBn=uf6+>_2TeevQx348Qx>=&u!^Ag2A{d6{^Cnq7(%+dB%LFD5Q=N9+;}`(H`{X-8 zv4gt%X2m)Knb&o|l-j3?f@^lF+`}qOI=)nec|W m9mFYB8@wGvU=V?#2>c&c+9i&S_hbRarRjNQhI)oN_ zM<5_dks>7o^yYZJ`~8Re+nPgjfjbkii(el z14aQ7L*g>MlE8im$uUXkF=^l!@WI#vxo&Cs&%lR0(u$oA9*jSb8JCeAmsP+jC`~9S zPbeE#s;IWgYP86ywLa9XS1_tk0T=3-SExTK)v_bjJrq-mE_LR2Hl#cF{uHLks!L)(VjFIt-vB`{y#jJ((oVCrIjl;Zy zMwB9NeK9+O7ltqz6Mi@hNih z?$iwH(u(L+jqFu_*{2@Wry13!72T&zL`=VKY`9JjFHEgBX*f1_OC}Bh{zgr%^ZD# z7;{99IU&a&*<;SxV=g)4&e`M6IpeOmShqZ^dmh#!ANzOy#NP!IUWJq1MUy^7lfK21 zPl-TH`Jtx#QB%*}OheyJ2b9bNmd*r~&IG@k2`QU_mCwQ|=HTUXp%rtX?<3YDBG)5d zuD^`gh>6>XOW90G-Aql}Ow0P3h5U-l{+gY$os+wro41{pznx$Btq}DMH5dM2KC*KD zWz~FC)k1XjLQKtKTl(A5emGIc3e|c9HTQLe20Hq?kUVwt z@PUf^xInpq;*#PrS4KCtNl2LG^)%Hif*khp;}`9%En^!(nEeVm={0-Vn91|BuA9;^ zyV1wysAas&wd;WJ(KhN(SE872||Cx z&kz&`1J8UbpY6Br1fH^;vhB0%CcTWq`%6fIX$7`0pPl)^4woBk5;Kl>z%scb&w@NY zbP1zjS{s-x7huwZN8Jd7!ebc@+dlRWYZycbI5fMo_Ain1qp_9SI})CCkpPuQvE1L< z5?LQ30nK&umzFYGv=gEn(&XPMt1A`F&{({1OLp}pTfhIXtRq^ zS=Xb=sYUjp<34RB*k3 zes-`!Phw2zF{`xAHf9#0*1vk(6#Yl}IlQ27-Iq$Q4?1(Y`Tl(I$k&-I!)6hYORUQN zQ{BRJJW-86q`ML~Yd}ZxwZG;abKV$0?qXp0>(q2Gp0GQsx;c{BF6cH^QZnp~7$8In z11rAN(v=o~pg9e!&a`~M!-)A0`-$FQtF^w1BQTE5+y-UcqEfE0Of=Ze~I^qxhb@|wjXX9+3hE_U%6Ycf;z~-C;jQg$lzNr_MJxq!A3hU;ee-gXf z6fge|fgSDDF{yBE1;NET(`C`;Zgsf%96W1#tr7s>!#N5=fbY-iu9j!$J|9z%n6tgH z=|}9{`V#x*8vsn`-|i$5Trd2|uit7rU6d$UD;;6^%_g<<`CoBbLg1xPDR8MCuh!G4 zRVt+|J&E}JW?gADR^%06HDb!|Re2J2_$U8jrx*Badae#%r=tG-2|GmbJ-+VB&}+W! zMsci1(&2}%nA6;Lu~jYePGDcq*2R&LzWDjenJnL6v5|sF14xov6`=6#R{D=X^d&9L z=l<+I)-gM9J@b4aY{c~#<^V!=Av<%>z}#PMZUiS914n}=jR0BmpK)K?6IjwA|Gv-D zcWj*DISyIPSs9UgiJy4Qc~+5;#29WlMCb9XBD-MVQS&n%Wi_;9G#;I_C*jPIAwdjp zDj1%M65KBX_=w!Y%#DD$H4L1R+zJ4`@75u57krHD_p;Y8j7%@s@2m9V`d9@B{#o*4 zU;g?i5tF?Q3V}~(?o!V`62KqP4z-3aB43-jMFL)A2Ji%AOt5Mbj<{XyBc2UU2ue>N z-U)q^=X9*nPV+j$=Qu<#_dP`ifc$BpRgSZ`tUX!4Af27AtxatW}&4US@^! zSwRBIA)NXwRXXQ!w#kO8KeZYbks$VQbk;h?--{!|{HUa@eFpHfMev5F`1iADM1Et2 z6x(&Mk!fw-<~GO*Afa6ZAl&uEaZ3=PZq z+XWU!#wvMP_~9fWM#V64%@kg2C3JY&(q^T9F2NY2&mrCwqjS7TQKBEDu$HCw)fsga z-t{0NfR(ZM+&@R3Ur~2X5^sPLQDo7Zm%k8VRVI5Pa zUx>0ayn3|L#c{jztYbu|{pmyfsJ)EmeCV=34kd%9(K`bdjQzKYH5HASv9H;^p0oNY%l3_4!TqShc#g|&W=S4it@%+`*RmMoi*u>~u$M30M%GLM8fBe`3 zZ#}RJ#Z~FtU3;#5Eb!vRNElFB{72!!q)OkfD~~U~G9-KPeQ1+>3U!fr-lUdfIyJBG zpk@M7#TVgXSOzmXdJb>?wlfqs5psMr5V^-Y^yc5)mQ2rUZ}BY)97Qwf zoqe-z({16%EWV>wmK0jWt;`1Js<@ZTEpGxW-;{Dvw^Qx8&6hIwQa%ktd8p0<$KI%- z_$HFy0@W$6=O-QO+_y4eTQ-{?4~&R8juUgI%}fXMP6s#xbI2=Q>Hvqbsc5<^C}g1C zIx@S0;2Z@VYSaXa9P#JeOD8S)0>Wm^vGR?<zt3m7|&1Wo6=*Sx%?;*(>1mP zQM$zxfi?Qk1OCMii99-@5oG?#Q}ae{f>hcMqLEPpo$r=ba%iyNa(js_?&{N0xrPSh z8xiaEL_wAWQmu@bnOj#3;dH8h81Ns7{@;~Er%wpoRA;zly+`+{2LxM#x%e{8e~6>( z;sGc-3R)&Zc3YAQ{_ubz@LyZ_3Gd<%aGY+OdvWsPLDRBRHvi1vcRKV+d_kkl3wF~@ z2ju{G3mvCm4m76moqcYhivIf&j$G7ogdz^qu|qf3MA&}--sd;lnOg5LFj|{(1I+~p zyH&)x`z&k>l-O85ic^`|{ z;tFZ0yg~CwlZs0+508|j8jVmuiCs3p_KvBxJ}sZloO#JSBYK7mj5Fwpd4GJ}UR5=| z(}saP{mNc)$WkVTY%`5U(=1N@;?8u?NlYezVMuE-0;P)z$GXwyMxp>iilyfaTMD}% zzqYy`V!j&mZLdsvkrJVL=BnJ$Wu(0?w^Rq=H|cF@nY4tNO}uUnp=;K2*Avg1%K?OK zC0|~3={Fy>RIEF$1lQK7)tQe7g_KR?49|;##yJHO08mz2T7bJ*d3b`6`8wf9AC1dp zv10DM2I8txO#eW5uDQUri&4`1`0i=0lqq=v`A7unTsD?MpN~%?Ej(G^aWKfz!)Jy`m zsVXx~j=x{^_3NRsO{p1mxwlmhi{wmzU|37gp1U-MyS&NmiwW^-<}fvA5gGWX!Lpe% z+m#Y~@dU(*5~`$vTi7r%{4dJ_yx@PhfOVA_hZc$cEV19df6l7|PmKiN`MWunm}k_W zh?Gguu5edJ7ad?nmcjmwv;PdR0HG=K%0_C=;xt zRIZlbHec#g)aQ;V`eSk|-0w~dg0@gz0y*3x7k6-&EqaLANWx=I*;q1L^Z&Hy8S+NGS=YWeeJ|A-zXJr(GLR) z$Ws9_r#LwiMR!^z(>eG0(>9q~IkcI_ zt`}n*&(D0x*1L{`qnADpzKDSd z+1-{HV)65idxN9W`1A?ztIYqPXkz1x{^vaqY%q|!tw41+VqNW7QBSm3X5tbvJng;OZecnibrch3sn;Rmm5K0l#~4jyMM%G!an22Sqs&}sZTS?B2Kq3QonP;e{? zfUR+c^_veOp7P%wA>$HJ)D_rTRz_Gwro7fMg#7CnnQb-oe9A?J;&w?^00@j;E}{;A z?N>j_gx$2MXP6Tyvg~!c9Ww!|x!jlR;S{_JB-h_hG!y3<7qF(BiE*bn(tUhkYtBulko z>xIwTx(q+=CU1Vk7QWy?*IZH#c|7VxwApRjdU(j=)RS-&R5uLC^_AO9j`=V4ojke` zf=gX4!K4U-abYuK$V0qoj?#s1_fUStFOh+01!^e|wDYF=tUl=D^>Vd>1FZ>x(EvyV zdVu!c%KL55RnMbs2|R^9*_fm7GGzbFP_-+aZk#a3{#=SU6Ae|<7RXB6xw2lWp_NHJ zVcC}w8&Cw~CaXYe4qnBIT`;V~Slw8I*|0=m?z_7(b(5LJg(Nq-1@Se#OMgd5N8hT* zG({19bGR+VpPyI7`20jPl9up%C)Gf2-*FvBObo8qSX;LYp1|K``b<%%NEs3Lcz!&Z zZMkV0t|y>l2?DiJcw7gk5AF~<%wPSCxV*Np7Lf&%?ixXS&*PGwas}dIDP?)A9Q7JQ8x1 z6c1w*L9IAm03ILki?g5{Y@`0-d%*qB#5Prtl-Q-?116ZMGDXCZVLD$~zvNXV1BD#t zLguQCno7nxea8>$1O8AEpb!ekG+Gke^MyKrv_$KX#BSn@qcA#((BL7Q`lXq?Tx&^J zMzO&^OrD-b3OMjI6Ba)dZ6)a4_AMp#9W0ILcJ*02U}yhF&GO*P`^I!aoVM}<;rGW@ zLM}hoNRl%!Cy>8B;W8CGLHY#F@MLukAht(R-^NTZ1PjK;NMVWm`PCb6dM;T`#B;RZ z2~Uxx{EdQ8ix&@X3%x#@#(iSz&F)sq7Rj5uofIq*|7)oDf_ z_S7qUN*OsmJtgql=FLbueYQ1+SgwHFkZRYotB)jTbU_O!E5FX$$FD7z?qcM ziH_vrnpXL2LAy?K)IutPAZN0{g)e=64|W7(b1DG#@xrpEA?aM&>@eZrKxt|3n`zyz z4mROaS`y#<;J56&X2@7JZ*{(QY+M*RKAyQr3+zHH)IDDOImYevv$8ca6)i}JUgDQ@ zGL(t-5i7lX>&5y@4yOyQr$_kY8ogjfmV|IU@$=e!$^$p`G!@^o3NEy7qAKPBEj;+KDUC+JpfL((@dtdCllQmuyZZle6BE4|1a*2#I41I_|2 zy5Y!ssL*4H_>M#NC$UEC4_&xvp@6`schPWEHjuj~ zDwiT@n5F&6+B}?widLf4zIAz@)r?wA<4`M17}YEXnKBm#e)^2)ddu{hOJ~!-_K#rV7!=1&+l#ngX08r(?CyMaVTLNSQU&q+T`!2< z-A9=TMJX}n#oGxfPB*o>&$we@ru%_Ed)zv83Nq3>YwEr>fH&ll%Fs+HX1JM-wTmb3 zT0W|zdYJ;QJansmNkYL1ea?>~M3_DI4Rl&pw76r*?wK5vwDnJe*QbhupYd=-ScG!( zJW)9)ARhApCAj6c&UR0kbfS)1yX1GnrQYBY1D+f7CxNU`J41{G-2K}^7iN{X)4S=~ z=_(}JMGjws=7b1R2go#>b&iOld8@&ENTN|MA|%Bl2-zn5-S~p4*=0Vi9A8J^ZV-9a z7?R&pvZyNpnYG!rfR}UjQ|$ku7)*B?ot<@1ZA_E;f)Ru$_OFIqtUUWVx}Tr(ls#Nl zQi^y0|2CNRPdB}zA+e1?t}+z%L)s#WC3Z2LzLq1s+QRRX4oglyq_sqGRRf{gcqx}- zl?2G}jG*KMVi|0XwG2{`evpuK;?e}VEA*f+9^x6Baftp. -*/ -function toHexString(byteArray) { - // cc-by-sa-4 https://stackoverflow.com/a/44608819 by https://stackoverflow.com/users/1883624/grantpatterson - var s = '0x' - byteArray.forEach(function(byte) { - s += ('0' + (byte & 0xFF).toString(16)).slice(-2) - }) - return s - } - - async function sha256(str) { - const buf = await crypto.subtle.digest("SHA-256", new TextEncoder("utf-8").encode(str)) - return Array.prototype.map.call(new Uint8Array(buf), x=>(('00'+x.toString(16)).slice(-2))).join('') - } - -async function userIcon(pubkey, imgSize=64){ - pubkey = await sha256(base32.decode.asBytes(pubkey)) - let options = { - //foreground: [0,0,0,1], // rgba black - background: [0, 0, 0, 0], // rgba white - //margin: 0.1, - size: imgSize, - format: 'svg' // use SVG instead of PNG - }; - - // create a base64 encoded SVG - let data = new Identicon(pubkey, options).toString(); - return data -} \ No newline at end of file