Removed more defunct code
This commit is contained in:
parent
e1b4b12024
commit
cb977a2719
@ -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
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
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)
|
@ -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()
|
||||
|
@ -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)
|
@ -1 +0,0 @@
|
||||
from urllib3.contrib.socks import SOCKSProxyManager # noqa
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
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
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
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
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
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
|
||||
|
@ -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.")
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
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
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
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))
|
||||
|
@ -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)
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
|
||||
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
|
@ -1 +0,0 @@
|
||||
https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/robots.txt,http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/robots.txt,http://rurcblzhmdk22kttfkel2zduhyu3r6to7knyc7wiorzrx5gw4c3lftad.onion/
|
Binary file not shown.
@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>
|
||||
Onionr
|
||||
</title>
|
||||
<link rel="shortcut icon" type="image/ico" href="/shared/images/favicon.ico">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>Onionr</p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -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 = "<svg xmlns='http://www.w3.org/2000/svg'"
|
||||
+ " width='" + this.size + "' height='" + this.size + "'"
|
||||
+ " style='background-color:" + bg + ";'>"
|
||||
+ "<g style='fill:" + fg + "; stroke:" + fg + "; stroke-width:" + stroke + ";'>";
|
||||
|
||||
for (i = 0; i < this.rectangles.length; i++) {
|
||||
rect = this.rectangles[i];
|
||||
if (rect.color == bg) continue;
|
||||
xml += "<rect "
|
||||
+ " x='" + rect.x + "'"
|
||||
+ " y='" + rect.y + "'"
|
||||
+ " width='" + rect.w + "'"
|
||||
+ " height='" + rect.h + "'"
|
||||
+ "/>";
|
||||
}
|
||||
xml += "</g></svg>"
|
||||
|
||||
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;
|
||||
}
|
||||
})();
|
Binary file not shown.
Before Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.5 KiB |
@ -1,46 +0,0 @@
|
||||
/*
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Provides userIcon which generates SVG identicons from a Onionr user pubkey
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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
|
||||
}
|
Loading…
Reference in New Issue
Block a user