Removed webUI and unused god objects
Remove defunct requirements Removed more defunct code Prepare onionrvalues for new release disable unixtransport by default
This commit is contained in:
parent
228d3cbe35
commit
c880b6fa7a
@ -1,17 +1,10 @@
|
||||
urllib3==1.26.7
|
||||
requests==2.28.1
|
||||
PyNaCl==1.5.0
|
||||
gevent==21.12.0
|
||||
Flask==2.1.3
|
||||
PySocks==1.7.1
|
||||
stem==1.8.0
|
||||
deadsimplekv==0.3.2
|
||||
unpaddedbase32==0.2.0
|
||||
toomanyobjs==1.1.0
|
||||
niceware==0.2.1
|
||||
psutil==5.9.1
|
||||
filenuke==0.0.0
|
||||
watchdog==2.1.9
|
||||
ujson==5.4.0
|
||||
cffi==1.15.1
|
||||
onionrblocks==7.0.0
|
||||
|
@ -4,36 +4,14 @@
|
||||
#
|
||||
# pip-compile
|
||||
#
|
||||
certifi==2018.11.29
|
||||
# via requests
|
||||
cffi==1.15.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pynacl
|
||||
charset-normalizer==2.0.9
|
||||
# via requests
|
||||
click==8.0.3
|
||||
# via flask
|
||||
deadsimplekv==0.3.2
|
||||
# via -r requirements.in
|
||||
filenuke==0.0.0
|
||||
# via -r requirements.in
|
||||
flask==2.1.3
|
||||
# via -r requirements.in
|
||||
gevent==21.12.0
|
||||
# via -r requirements.in
|
||||
greenlet==1.1.2
|
||||
# via gevent
|
||||
idna==2.7
|
||||
# via requests
|
||||
itsdangerous==2.0.1
|
||||
# via flask
|
||||
jinja2==3.0.3
|
||||
# via flask
|
||||
kasten==3.0.0
|
||||
# via onionrblocks
|
||||
markupsafe==2.0.1
|
||||
# via jinja2
|
||||
mimcvdf==1.2.1
|
||||
# via kasten
|
||||
msgpack==1.0.3
|
||||
@ -54,28 +32,9 @@ pynacl==1.5.0
|
||||
# onionrblocks
|
||||
pysocks==1.7.1
|
||||
# via -r requirements.in
|
||||
requests==2.28.1
|
||||
# via -r requirements.in
|
||||
stem==1.8.0
|
||||
# via -r requirements.in
|
||||
toomanyobjs==1.1.0
|
||||
# via -r requirements.in
|
||||
ujson==5.4.0
|
||||
# via -r requirements.in
|
||||
unpaddedbase32==0.2.0
|
||||
# via -r requirements.in
|
||||
urllib3==1.26.7
|
||||
# via
|
||||
# -r requirements.in
|
||||
# requests
|
||||
watchdog==2.1.9
|
||||
# via -r requirements.in
|
||||
werkzeug==2.0.2
|
||||
# via flask
|
||||
zope-event==4.4
|
||||
# via gevent
|
||||
zope-interface==5.1.0
|
||||
# via gevent
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# setuptools
|
||||
|
@ -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,9 +0,0 @@
|
||||
# API Servers
|
||||
|
||||
Contains the WSGI servers Onionr uses for remote peer communication and local daemon control
|
||||
|
||||
## Files
|
||||
|
||||
* \_\_init\_\_.py: Exposes the server classes
|
||||
* private: Contains the client API (the server used to interact with the local Onionr daemon, and view the web UI)
|
||||
* public: Contains the public API (the server used by remote peers to talk to our daemon)
|
@ -1,9 +0,0 @@
|
||||
"""Flask WSGI apps for the public and private API servers.
|
||||
|
||||
Public is net-facing server meant for other nodes
|
||||
Private is meant for controlling and accessing this node
|
||||
"""
|
||||
|
||||
from . import private
|
||||
|
||||
private_api = private.private_api
|
@ -1,8 +0,0 @@
|
||||
# Private API Server
|
||||
|
||||
Private API server, used to access the web interface locally and control Onionr
|
||||
|
||||
## Files
|
||||
|
||||
* \_\_init\_\_.py: Sets up the server and a few misc functions
|
||||
* register_private_blueprints.py: Adds in flask blueprints for various sub-APIs
|
@ -1,121 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
This file handles all incoming http requests to the client, using Flask
|
||||
"""
|
||||
from typing import Dict
|
||||
from typing import Set
|
||||
from typing import TYPE_CHECKING
|
||||
import hmac
|
||||
|
||||
import flask
|
||||
from gevent.pywsgi import WSGIServer
|
||||
|
||||
from onionrutils import epoch
|
||||
import httpapi
|
||||
from filepaths import private_API_host_file
|
||||
import logger
|
||||
|
||||
from onionrutils import waitforsetvar
|
||||
from . import register_private_blueprints
|
||||
import config
|
||||
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
|
||||
class PrivateAPI:
|
||||
"""Client HTTP api for controlling onionr and using UI."""
|
||||
|
||||
callbacks: Dict[str, Dict] = {'public': {}, 'private': {}}
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the api server, preping variables for later use.
|
||||
|
||||
This initialization defines all of the API entry points
|
||||
and handlers for the endpoints and errors
|
||||
This also saves the used host (random localhost IP address)
|
||||
to the data folder in host.txt
|
||||
"""
|
||||
self.config = config
|
||||
|
||||
self.startTime = epoch.get_epoch()
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
|
||||
self.httpServer = ''
|
||||
|
||||
self.queueResponse = {}
|
||||
register_private_blueprints.register_private_blueprints(self, app)
|
||||
httpapi.load_plugin_blueprints(app)
|
||||
self.app = app
|
||||
|
||||
def start(self):
|
||||
"""Start client gevent API web server with flask client app."""
|
||||
|
||||
fd_handler = httpapi.fdsafehandler.FDSafeHandler
|
||||
|
||||
self.clientToken = config.get('client.webpassword')
|
||||
if config.get('general.bind_address'):
|
||||
with open(private_API_host_file, 'w') as bindFile:
|
||||
bindFile.write(config.get('general.bind_address'))
|
||||
self.host = config.get('general.bind_address')
|
||||
else:
|
||||
self.host = httpapi.apiutils.setbindip.set_bind_IP(
|
||||
private_API_host_file)
|
||||
bind_port = int(config.get('client.client.port', 59496))
|
||||
self.bindPort = bind_port
|
||||
|
||||
self.httpServer = WSGIServer((self.host, self.bindPort),
|
||||
self.app, log=None,
|
||||
handler_class=fd_handler)
|
||||
logger.info(f'Running API on {self.host}:{self.bindPort}', terminal=True)
|
||||
self.httpServer.serve_forever()
|
||||
|
||||
def setPublicAPIInstance(self, inst):
|
||||
"""Dynamically set public API instance."""
|
||||
self.publicAPI = inst
|
||||
|
||||
def validateToken(self, token):
|
||||
"""Validate that the client token matches the given token.
|
||||
|
||||
Used to prevent CSRF and other attacks.
|
||||
"""
|
||||
if not self.clientToken:
|
||||
logger.error("client password needs to be set")
|
||||
return False
|
||||
try:
|
||||
return hmac.compare_digest(self.clientToken, token)
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
def getUptime(self) -> int:
|
||||
"""Safely wait for uptime to be set and return it."""
|
||||
while True:
|
||||
try:
|
||||
return epoch.get_epoch() - self.startTime
|
||||
except (AttributeError, NameError):
|
||||
# Don't error on race condition with startup
|
||||
pass
|
||||
|
||||
def getBlockData(self, bHash, decrypt=False, raw=False,
|
||||
headerOnly=False) -> bytes:
|
||||
"""Returns block data bytes."""
|
||||
return self.get_block_data.get_block_data(bHash,
|
||||
decrypt=decrypt,
|
||||
raw=raw,
|
||||
headerOnly=headerOnly)
|
||||
|
||||
|
||||
private_api = PrivateAPI()
|
@ -1,46 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
This file registers blueprints for the private api server
|
||||
"""
|
||||
from threading import Thread
|
||||
from gevent import sleep
|
||||
|
||||
from httpapi import security, friendsapi, configapi
|
||||
from httpapi import miscclientapi, apiutils
|
||||
from httpapi import themeapi
|
||||
from httpapi import fileoffsetreader
|
||||
from httpapi.sse.private import private_sse_blueprint
|
||||
from httpapi import addblock
|
||||
|
||||
"""
|
||||
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 register_private_blueprints(private_api, app):
|
||||
"""Register private API plask blueprints."""
|
||||
app.register_blueprint(security.client.ClientAPISecurity(
|
||||
private_api).client_api_security_bp)
|
||||
app.register_blueprint(friendsapi.friends)
|
||||
app.register_blueprint(configapi.config_BP)
|
||||
app.register_blueprint(miscclientapi.endpoints.PrivateEndpoints(
|
||||
private_api).private_endpoints_bp)
|
||||
app.register_blueprint(apiutils.shutdown.shutdown_bp)
|
||||
app.register_blueprint(miscclientapi.staticfiles.static_files_bp)
|
||||
app.register_blueprint(themeapi.theme_blueprint)
|
||||
app.register_blueprint(private_sse_blueprint)
|
||||
app.register_blueprint(fileoffsetreader.offset_reader_api)
|
||||
app.register_blueprint(addblock.blockapi_blueprint)
|
||||
|
||||
return app
|
@ -20,6 +20,7 @@ upload_list = home + 'upload-list.json'
|
||||
config_file = home + 'config.json'
|
||||
daemon_mark_file = home + '/daemon-true.txt'
|
||||
lock_file = home + 'onionr.lock'
|
||||
pid_file = home + 'onionr.pid'
|
||||
|
||||
site_cache = home + 'onionr-sites.txt'
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
# httpapi
|
||||
|
||||
The httpapi contains collections of endpoints for the client and public API servers.
|
||||
|
||||
## Files:
|
||||
|
||||
configapi: manage onionr configuration from the client http api
|
||||
|
||||
friendsapi: add, remove and list friends from the client http api
|
||||
|
||||
miscpublicapi: misculanious onionr network interaction from the **public** httpapi, such as announcements, block fetching and uploading.
|
||||
|
||||
profilesapi: work in progress in returning a profile page for an Onionr user
|
@ -1,36 +0,0 @@
|
||||
"""
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Register plugins flask blueprints for the client http server
|
||||
"""
|
||||
import onionrplugins
|
||||
import config
|
||||
from .fdsafehandler import FDSafeHandler
|
||||
"""
|
||||
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 load_plugin_blueprints(flaskapp, blueprint: str = 'flask_blueprint'):
|
||||
"""Iterate enabled plugins and load any http endpoints they have"""
|
||||
config.reload()
|
||||
disabled = config.get('plugins.disabled', [])
|
||||
for plugin in onionrplugins.get_enabled_plugins():
|
||||
if plugin in disabled:
|
||||
continue
|
||||
plugin = onionrplugins.get_plugin(plugin)
|
||||
try:
|
||||
flaskapp.register_blueprint(getattr(plugin, blueprint))
|
||||
except AttributeError:
|
||||
pass
|
@ -1,45 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Serialized APIs
|
||||
"""
|
||||
|
||||
from asyncio.log import logger
|
||||
import secrets
|
||||
from flask import Blueprint, Response, request
|
||||
|
||||
from onionrblocks import Block
|
||||
import blockdb
|
||||
|
||||
import logger
|
||||
from gossip import blockqueues
|
||||
from gossip.constants import BLOCK_ID_SIZE
|
||||
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
blockapi_blueprint = Blueprint('blockapi', __name__)
|
||||
|
||||
|
||||
|
||||
# Add a block that we generated (or received from a transport like LAN/sneakernet)
|
||||
@blockapi_blueprint.route('/addvdfblock', methods=['POST'])
|
||||
def block_serialized():
|
||||
stream_to_use = secrets.randbits(1)
|
||||
req_data = request.data
|
||||
block_id = req_data[:BLOCK_ID_SIZE]
|
||||
block_data = req_data[BLOCK_ID_SIZE:]
|
||||
#blockdb.add_block_to_db(Block(block_id, block_data, auto_verify=False))
|
||||
blockqueues.gossip_block_queues[stream_to_use].put(
|
||||
Block(block_id, block_data, auto_verify=False), block=False)
|
||||
return "ok"
|
@ -1 +0,0 @@
|
||||
from . import shutdown, setbindip
|
@ -1,42 +0,0 @@
|
||||
import gevent
|
||||
from gevent import socket, sleep
|
||||
import secrets, random
|
||||
import config, logger
|
||||
import os
|
||||
|
||||
# Hacky monkey patch so we can bind random localhosts without gevent trying to switch with an empty hub
|
||||
socket.getfqdn = lambda n: n
|
||||
|
||||
def _get_acceptable_random_number()->int:
|
||||
"""Return a cryptographically random number in the inclusive range (1, 255)"""
|
||||
number = 0
|
||||
while number == 0:
|
||||
number = secrets.randbelow(0xFF)
|
||||
return number
|
||||
|
||||
def set_bind_IP(filePath=''):
|
||||
'''Set a random localhost IP to a specified file (intended for private or public API localhost IPs)'''
|
||||
if config.get('general.random_bind_ip', True):
|
||||
hostOctets = []
|
||||
# Build the random localhost address
|
||||
for i in range(3):
|
||||
hostOctets.append(str(_get_acceptable_random_number()))
|
||||
hostOctets = ['127'] + hostOctets
|
||||
# Convert the localhost address to a normal string address
|
||||
data = '.'.join(hostOctets)
|
||||
|
||||
# Try to bind IP. Some platforms like Mac block non normal 127.x.x.x
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.bind((data, 0))
|
||||
except OSError:
|
||||
# if mac/non-bindable, show warning and default to 127.0.0.1
|
||||
logger.warn('Your platform appears to not support random local host addresses 127.x.x.x. Falling back to 127.0.0.1.')
|
||||
data = '127.0.0.1'
|
||||
s.close()
|
||||
else:
|
||||
data = '127.0.0.1'
|
||||
if filePath != '':
|
||||
with open(filePath, 'w') as bindFile:
|
||||
bindFile.write(data)
|
||||
return data
|
@ -1,30 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Shutdown the node either hard or cleanly
|
||||
"""
|
||||
from flask import Blueprint, Response
|
||||
from flask import g
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
shutdown_bp = Blueprint('shutdown', __name__)
|
||||
|
||||
|
||||
def shutdown(client_api_inst):
|
||||
try:
|
||||
client_api_inst.httpServer.stop()
|
||||
except AttributeError:
|
||||
pass
|
||||
return Response("bye")
|
@ -1,66 +0,0 @@
|
||||
"""
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This file handles configuration setting and getting from the HTTP API
|
||||
"""
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
from json import JSONDecodeError
|
||||
import ujson as json
|
||||
from flask import Blueprint, request, Response, abort
|
||||
|
||||
import config, onionrutils
|
||||
|
||||
from onionrutils.bytesconverter import bytes_to_str
|
||||
config.reload()
|
||||
|
||||
config_BP = Blueprint('config_BP', __name__)
|
||||
|
||||
@config_BP.route('/config/get')
|
||||
def get_all_config():
|
||||
"""Simply return all configuration as JSON string"""
|
||||
return Response(json.dumps(config.get_config(), indent=4, sort_keys=True))
|
||||
|
||||
@config_BP.route('/config/get/<key>')
|
||||
def get_by_key(key):
|
||||
"""Return a config setting by key"""
|
||||
return Response(json.dumps(config.get(key)))
|
||||
|
||||
@config_BP.route('/config/setall', methods=['POST'])
|
||||
def set_all_config():
|
||||
"""Overwrite existing JSON config with new JSON string"""
|
||||
try:
|
||||
new_config = request.get_json(force=True)
|
||||
except JSONDecodeError:
|
||||
abort(400)
|
||||
else:
|
||||
config.set_config(new_config)
|
||||
config.save()
|
||||
return Response('success')
|
||||
|
||||
@config_BP.route('/config/set/<key>', methods=['POST'])
|
||||
def set_by_key(key):
|
||||
"""Overwrite/set only 1 config key"""
|
||||
"""
|
||||
{
|
||||
'data': data
|
||||
}
|
||||
"""
|
||||
try:
|
||||
data = json.loads(bytes_to_str(request.data))
|
||||
except (JSONDecodeError, KeyError):
|
||||
abort(400)
|
||||
config.set(key, data, True)
|
||||
return Response('success')
|
@ -1,16 +0,0 @@
|
||||
from gevent.pywsgi import WSGIServer, WSGIHandler
|
||||
from gevent import Timeout
|
||||
|
||||
|
||||
class FDSafeHandler(WSGIHandler):
|
||||
'''Our WSGI handler. Doesn't do much non-default except timeouts'''
|
||||
def handle(self):
|
||||
self.timeout = Timeout(120, Exception)
|
||||
self.timeout.start()
|
||||
try:
|
||||
WSGIHandler.handle(self)
|
||||
except Timeout as ex:
|
||||
if ex is self.timeout:
|
||||
pass
|
||||
else:
|
||||
raise
|
@ -1,30 +0,0 @@
|
||||
from os.path import exists, dirname
|
||||
|
||||
import ujson
|
||||
from flask import Blueprint, Response, request
|
||||
|
||||
from utils.identifyhome import identify_home
|
||||
from utils.readoffset import read_from_offset
|
||||
|
||||
offset_reader_api = Blueprint('offsetreaderapi', __name__)
|
||||
|
||||
|
||||
@offset_reader_api.route('/readfileoffset/<name>')
|
||||
def offset_reader_endpoint(name):
|
||||
if not name[:-4].isalnum():
|
||||
return Response(400, "Path must be alphanumeric except for file ext")
|
||||
|
||||
path = identify_home() + name
|
||||
|
||||
if not exists(path):
|
||||
return Response(404, "Path not found in Onionr data directory")
|
||||
|
||||
offset = request.args.get('offset')
|
||||
|
||||
if not offset:
|
||||
offset = 0
|
||||
else:
|
||||
offset = int(offset)
|
||||
result = read_from_offset(path, offset)._asdict()
|
||||
|
||||
return ujson.dumps(result, reject_bytes=False)
|
@ -1,75 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
This file creates http endpoints for friend management
|
||||
"""
|
||||
import ujson as json
|
||||
|
||||
from onionrusers import contactmanager
|
||||
from flask import Blueprint, Response, request, abort, redirect
|
||||
from coredb import keydb
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
friends = Blueprint('friends', __name__)
|
||||
|
||||
|
||||
@friends.route('/friends/list')
|
||||
def list_friends():
|
||||
pubkey_list = {}
|
||||
friend_list = contactmanager.ContactManager.list_friends()
|
||||
for friend in friend_list:
|
||||
pubkey_list[friend.publicKey] = {'name': friend.get_info('name')}
|
||||
return json.dumps(pubkey_list)
|
||||
|
||||
|
||||
@friends.route('/friends/add/<pubkey>', methods=['POST'])
|
||||
def add_friend(pubkey):
|
||||
contactmanager.ContactManager(pubkey, saveUser=True).setTrust(1)
|
||||
try:
|
||||
return redirect(request.referrer + '#' + request.form['token'])
|
||||
except TypeError:
|
||||
return Response(
|
||||
"Added, but referrer not set, cannot return to friends page")
|
||||
|
||||
|
||||
@friends.route('/friends/remove/<pubkey>', methods=['POST'])
|
||||
def remove_friend(pubkey):
|
||||
contactmanager.ContactManager(pubkey).setTrust(0)
|
||||
contactmanager.ContactManager(pubkey).delete_contact()
|
||||
keydb.removekeys.remove_user(pubkey)
|
||||
try:
|
||||
return redirect(request.referrer + '#' + request.form['token'])
|
||||
except TypeError:
|
||||
return Response(
|
||||
"Friend removed, but referrer not set, cannot return to page")
|
||||
|
||||
|
||||
@friends.route('/friends/setinfo/<pubkey>/<key>', methods=['POST'])
|
||||
def set_info(pubkey, key):
|
||||
data = request.form['data']
|
||||
contactmanager.ContactManager(pubkey).set_info(key, data)
|
||||
try:
|
||||
return redirect(request.referrer + '#' + request.form['token'])
|
||||
except TypeError:
|
||||
return Response(
|
||||
"Info set, but referrer not set, cannot return to friends page")
|
||||
|
||||
|
||||
@friends.route('/friends/getinfo/<pubkey>/<key>')
|
||||
def get_info(pubkey, key):
|
||||
ret_data = contactmanager.ContactManager(pubkey).get_info(key)
|
||||
if ret_data is None:
|
||||
abort(404)
|
||||
else:
|
||||
return ret_data
|
@ -1,35 +0,0 @@
|
||||
'''
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Set default onionr http headers
|
||||
'''
|
||||
'''
|
||||
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/>.
|
||||
'''
|
||||
FEATURE_POLICY = """vibrate; vr; webauthn; usb; sync-xhr; speaker;
|
||||
picture-in-picture; payment; midi; microphone; magnetometer; gyroscope;
|
||||
geolocation; fullscreen; encrypted-media; document-domain;
|
||||
camera; accelerometer; ambient-light-sensor""".replace('\n', '') # have to remove \n for flask
|
||||
def set_default_onionr_http_headers(flask_response):
|
||||
'''Response headers'''
|
||||
flask_response.headers['Content-Security-Policy'] = "default-src 'none'; style-src data: 'unsafe-inline'; img-src data:"
|
||||
flask_response.headers['X-Frame-Options'] = 'deny'
|
||||
flask_response.headers['X-Content-Type-Options'] = "nosniff"
|
||||
flask_response.headers['Server'] = ''
|
||||
flask_response.headers['Date'] = 'Thu, 1 Jan 1970 00:00:00 GMT' # Clock info is probably useful to attackers. Set to unix epoch.
|
||||
flask_response.headers['Connection'] = "close"
|
||||
flask_response.headers['Clear-Site-Data'] = '"cache", "cookies", "storage", "executionContexts"'
|
||||
flask_response.headers['Feature-Policy'] = FEATURE_POLICY
|
||||
flask_response.headers['Referrer-Policy'] = 'same-origin'
|
||||
return flask_response
|
@ -1 +0,0 @@
|
||||
from . import staticfiles, endpoints
|
@ -1,111 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Misc client API endpoints too small to need their own file and that need access to the client api inst
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import platform
|
||||
from sys import stdout as sys_stdout
|
||||
|
||||
from flask import Response, Blueprint, request, send_from_directory, abort
|
||||
from flask import g
|
||||
from gevent import sleep
|
||||
import unpaddedbase32
|
||||
|
||||
from httpapi import apiutils
|
||||
import onionrcrypto
|
||||
import config
|
||||
from onionrutils import mnemonickeys
|
||||
from onionrutils import bytesconverter
|
||||
import onionrvalues
|
||||
from utils import reconstructhash
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
pub_key = onionrcrypto.pub_key.replace('=', '')
|
||||
|
||||
SCRIPT_NAME = os.path.dirname(os.path.realpath(__file__)) + \
|
||||
f'/../../../{onionrvalues.SCRIPT_NAME}'
|
||||
|
||||
|
||||
class PrivateEndpoints:
|
||||
def __init__(self, client_api):
|
||||
private_endpoints_bp = Blueprint('privateendpoints', __name__)
|
||||
self.private_endpoints_bp = private_endpoints_bp
|
||||
|
||||
|
||||
@private_endpoints_bp.route('/www/<path:path>', endpoint='www')
|
||||
def wwwPublic(path):
|
||||
if not config.get("www.private.run", True):
|
||||
abort(403)
|
||||
return send_from_directory(config.get('www.private.path',
|
||||
'static-data/www/private/'), path)
|
||||
|
||||
@private_endpoints_bp.route('/getpid')
|
||||
def get_pid():
|
||||
return Response(str(os.getpid()))
|
||||
|
||||
@private_endpoints_bp.route('/isatty')
|
||||
def get_is_atty():
|
||||
return Response(str(sys_stdout.isatty()).lower())
|
||||
|
||||
|
||||
@private_endpoints_bp.route('/ping')
|
||||
def ping():
|
||||
# Used to check if client api is working
|
||||
return Response("pong!")
|
||||
|
||||
|
||||
@private_endpoints_bp.route('/shutdown')
|
||||
def shutdown():
|
||||
return apiutils.shutdown.shutdown(client_api)
|
||||
|
||||
@private_endpoints_bp.route('/restartclean')
|
||||
def restart_clean():
|
||||
subprocess.Popen([SCRIPT_NAME, 'restart'])
|
||||
return Response("bye")
|
||||
|
||||
|
||||
@private_endpoints_bp.route('/getuptime')
|
||||
def show_uptime():
|
||||
return Response(str(client_api.getUptime()))
|
||||
|
||||
@private_endpoints_bp.route('/getActivePubkey')
|
||||
def get_active_pubkey():
|
||||
return Response(pub_key)
|
||||
|
||||
@private_endpoints_bp.route('/getHumanReadable')
|
||||
def get_human_readable_default():
|
||||
return Response(mnemonickeys.get_human_readable_ID())
|
||||
|
||||
@private_endpoints_bp.route('/getHumanReadable/<name>')
|
||||
def get_human_readable(name):
|
||||
name = unpaddedbase32.repad(bytesconverter.str_to_bytes(name))
|
||||
return Response(mnemonickeys.get_human_readable_ID(name))
|
||||
|
||||
@private_endpoints_bp.route('/getBase32FromHumanReadable/<words>')
|
||||
def get_base32_from_human_readable(words):
|
||||
return Response(
|
||||
bytesconverter.bytes_to_str(mnemonickeys.get_base32(words)))
|
||||
|
||||
@private_endpoints_bp.route('/setonboarding', methods=['POST'])
|
||||
def set_onboarding():
|
||||
return Response(
|
||||
config.onboarding.set_config_from_onboarding(request.get_json()))
|
||||
|
||||
@private_endpoints_bp.route('/os')
|
||||
def get_os_system():
|
||||
return Response(platform.system().lower())
|
||||
|
@ -1,80 +0,0 @@
|
||||
"""Onionr - Private P2P Communication
|
||||
|
||||
Register static file routes
|
||||
"""
|
||||
import os
|
||||
import mimetypes
|
||||
from flask import Blueprint, send_from_directory
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
|
||||
# Was having some mime type issues on windows, this appeared to fix it.
|
||||
# we have no-sniff set, so if the mime types are invalid sripts can't load.
|
||||
mimetypes.add_type('application/javascript', '.js')
|
||||
mimetypes.add_type('text/css', '.css')
|
||||
|
||||
static_files_bp = Blueprint('staticfiles', __name__)
|
||||
|
||||
# should be set to onionr install directory from onionr startup
|
||||
root = os.path.dirname(os.path.realpath(__file__)) + \
|
||||
'/../../../static-data/www/'
|
||||
|
||||
|
||||
@static_files_bp.route('/onboarding/', endpoint='onboardingIndex')
|
||||
def onboard():
|
||||
return send_from_directory(f'{root}onboarding/', "index.html")
|
||||
|
||||
|
||||
@static_files_bp.route('/onboarding/<path:path>', endpoint='onboarding')
|
||||
def onboard_files(path):
|
||||
return send_from_directory(f'{root}onboarding/', path)
|
||||
|
||||
|
||||
@static_files_bp.route('/friends/<path:path>', endpoint='friends')
|
||||
def loadContacts(path):
|
||||
return send_from_directory(root + 'friends/', path)
|
||||
|
||||
|
||||
@static_files_bp.route('/friends/', endpoint='friendsindex')
|
||||
def loadContacts():
|
||||
return send_from_directory(root + 'friends/', 'index.html')
|
||||
|
||||
|
||||
@static_files_bp.route('/profiles/<path:path>', endpoint='profiles')
|
||||
def loadContacts(path):
|
||||
return send_from_directory(root + 'profiles/', path)
|
||||
|
||||
|
||||
@static_files_bp.route('/profiles/', endpoint='profilesindex')
|
||||
def loadContacts():
|
||||
return send_from_directory(root + 'profiles/', 'index.html')
|
||||
|
||||
|
||||
|
||||
@static_files_bp.route('/shared/<path:path>', endpoint='sharedContent')
|
||||
def sharedContent(path):
|
||||
return send_from_directory(root + 'shared/', path)
|
||||
|
||||
|
||||
@static_files_bp.route('/', endpoint='onionrhome')
|
||||
def hello():
|
||||
# ui home
|
||||
return send_from_directory(root + 'private/', 'index.html')
|
||||
|
||||
|
||||
@static_files_bp.route('/private/<path:path>', endpoint='homedata')
|
||||
def homedata(path):
|
||||
return send_from_directory(root + 'private/', path)
|
@ -1 +0,0 @@
|
||||
from . import client
|
@ -1,102 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Process incoming requests to the client api server to validate
|
||||
that they are legitimate and not DNSR/XSRF or other local adversary
|
||||
"""
|
||||
from ipaddress import ip_address
|
||||
import hmac
|
||||
|
||||
from flask import Blueprint, request, abort, g
|
||||
|
||||
from httpapi import httpheaders
|
||||
from . import pluginwhitelist
|
||||
import config
|
||||
import logger
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
# Be extremely mindful of this.
|
||||
# These are endpoints available without a password
|
||||
whitelist_endpoints = [
|
||||
'www', 'staticfiles.homedata',
|
||||
'staticfiles.sharedContent',
|
||||
'staticfiles.friends', 'staticfiles.friendsindex', 'siteapi.site',
|
||||
'siteapi.siteFile', 'staticfiles.onionrhome',
|
||||
'themes.getTheme', 'staticfiles.onboarding', 'staticfiles.onboardingIndex']
|
||||
|
||||
remote_safe_whitelist = ['www', 'staticfiles']
|
||||
|
||||
public_remote_enabled = config.get('ui.public_remote_enabled', False)
|
||||
public_remote_hostnames = config.get('ui.public_remote_hosts', [])
|
||||
|
||||
|
||||
class ClientAPISecurity:
|
||||
def __init__(self, client_api):
|
||||
client_api_security_bp = Blueprint('clientapisecurity', __name__)
|
||||
self.client_api_security_bp = client_api_security_bp
|
||||
self.client_api = client_api
|
||||
pluginwhitelist.load_plugin_security_whitelist_endpoints(
|
||||
whitelist_endpoints)
|
||||
|
||||
@client_api_security_bp.before_app_request
|
||||
def validate_request():
|
||||
"""Validate request has set password & is the correct hostname."""
|
||||
# For the purpose of preventing DNS rebinding attacks
|
||||
if ip_address(client_api.host).is_loopback:
|
||||
localhost = True
|
||||
if request.host != '%s:%s' % \
|
||||
(client_api.host, client_api.bindPort):
|
||||
localhost = False
|
||||
|
||||
if not localhost and public_remote_enabled:
|
||||
if request.host not in public_remote_hostnames:
|
||||
logger.warn(
|
||||
f'{request.host} not in {public_remote_hostnames}')
|
||||
abort(403)
|
||||
else:
|
||||
if not localhost:
|
||||
logger.warn(
|
||||
f'Possible DNS rebinding attack by {request.host}')
|
||||
abort(403)
|
||||
|
||||
# Static files for Onionr sites
|
||||
if request.path.startswith('/site/'):
|
||||
return
|
||||
|
||||
if request.endpoint in whitelist_endpoints:
|
||||
return
|
||||
|
||||
try:
|
||||
if not hmac.compare_digest(
|
||||
request.headers['token'], client_api.clientToken):
|
||||
if not hmac.compare_digest(
|
||||
request.form['token'], client_api.clientToken):
|
||||
abort(403)
|
||||
except KeyError:
|
||||
if not hmac.compare_digest(
|
||||
request.form['token'], client_api.clientToken):
|
||||
abort(403)
|
||||
|
||||
@client_api_security_bp.after_app_request
|
||||
def after_req(resp):
|
||||
# Security headers
|
||||
resp = httpheaders.set_default_onionr_http_headers(resp)
|
||||
if request.endpoint in ('siteapi.site', 'siteapi.siteFile'):
|
||||
resp.headers['Content-Security-Policy'] = \
|
||||
"default-src 'none'; style-src 'self' data: 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:" # noqa
|
||||
else:
|
||||
resp.headers['Content-Security-Policy'] = \
|
||||
"default-src 'none'; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self'; frame-src 'none'; font-src 'self'; connect-src 'self'" # noqa
|
||||
return resp
|
@ -1,67 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Process incoming requests to the public api server for certain attacks
|
||||
"""
|
||||
from flask import Blueprint, request, abort, g
|
||||
from httpapi import httpheaders
|
||||
from onionrutils import epoch
|
||||
from lan import getip
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
|
||||
class LANAPISecurity:
|
||||
def __init__(self, lan_client):
|
||||
lan_api_security_bp = Blueprint('lanapisecurity', __name__)
|
||||
self.lan_api_security_bp = lan_api_security_bp
|
||||
|
||||
@lan_api_security_bp.before_app_request
|
||||
def validate_request():
|
||||
"""Validate request has the correct hostname"""
|
||||
# If high security level, deny requests to public
|
||||
# (HS should be disabled anyway for Tor, but might not be for I2P)
|
||||
transports = getip.lan_ips
|
||||
if lan_client.config.get('general.security_level', default=1) > 0:
|
||||
abort(403)
|
||||
if request.host not in transports:
|
||||
# Abort conn if wrong HTTP hostname, to prevent DNS rebinding
|
||||
abort(403)
|
||||
lan_client.hitCount += 1 # raise hit count for valid requests
|
||||
try:
|
||||
if 'onionr' in request.headers['User-Agent'].lower():
|
||||
g.is_onionr_client = True
|
||||
else:
|
||||
g.is_onionr_client = False
|
||||
except KeyError:
|
||||
g.is_onionr_client = False
|
||||
|
||||
@lan_api_security_bp.after_app_request
|
||||
def send_headers(resp):
|
||||
"""Send api, access control headers"""
|
||||
resp = httpheaders.set_default_onionr_http_headers(resp)
|
||||
# Network API version
|
||||
resp.headers['X-API'] = lan_client.API_VERSION
|
||||
# Delete some HTTP headers for Onionr user agents
|
||||
NON_NETWORK_HEADERS = ('Content-Security-Policy', 'X-Frame-Options',
|
||||
'X-Content-Type-Options', 'Feature-Policy',
|
||||
'Clear-Site-Data', 'Referrer-Policy')
|
||||
try:
|
||||
if g.is_onionr_client:
|
||||
for header in NON_NETWORK_HEADERS:
|
||||
del resp.headers[header]
|
||||
except AttributeError:
|
||||
abort(403)
|
||||
lan_client.lastRequest = epoch.get_rounded_epoch(roundS=5)
|
||||
return resp
|
@ -1,33 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Load web UI client endpoints into the whitelist from plugins
|
||||
"""
|
||||
import onionrplugins
|
||||
"""
|
||||
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 load_plugin_security_whitelist_endpoints(whitelist: list):
|
||||
"""Accept a list reference of whitelist endpoints from security/client.py and
|
||||
append plugin's specified endpoints to them by attribute"""
|
||||
for plugin in onionrplugins.get_enabled_plugins():
|
||||
try:
|
||||
plugin = onionrplugins.get_plugin(plugin)
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
try:
|
||||
whitelist.extend(getattr(plugin, "security_whitelist"))
|
||||
except AttributeError:
|
||||
pass
|
@ -1,6 +0,0 @@
|
||||
# sse
|
||||
|
||||
This folder contains a wrapper for handling server sent event loops
|
||||
|
||||
|
||||
|
@ -1,18 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
server sent event modules, incl a wrapper and endpoints for client + public api
|
||||
"""
|
||||
"""
|
||||
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/>.
|
||||
"""
|
@ -1,41 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
SSE API for node client access
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
from flask import g, Blueprint
|
||||
from gevent import sleep
|
||||
import gevent
|
||||
import ujson
|
||||
|
||||
from onionrutils.epoch import get_epoch
|
||||
from .. import wrapper
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
private_sse_blueprint = Blueprint('privatesse', __name__)
|
||||
SSEWrapper = wrapper.SSEWrapper()
|
||||
|
||||
gevent.hub.Hub.NOT_ERROR = (gevent.GreenletExit, SystemExit, Exception)
|
||||
|
||||
@private_sse_blueprint.route('/hello')
|
||||
def stream_hello():
|
||||
def print_hello():
|
||||
while True:
|
||||
yield "hello\n\n"
|
||||
sleep(1)
|
||||
return SSEWrapper.handle_sse_request(print_hello)
|
||||
|
@ -1,34 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
wrapper for server sent event endpoints
|
||||
"""
|
||||
from typing import Callable
|
||||
|
||||
from flask import Response
|
||||
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
|
||||
class SSEWrapper:
|
||||
def __init__(self):
|
||||
self.active_count: int = 0
|
||||
|
||||
def handle_sse_request(self, handler: Callable):
|
||||
self.active_count += 1
|
||||
resp = Response(handler())
|
||||
resp.content_type = "text/event-stream"
|
||||
self.active_count -= 1
|
||||
return resp
|
@ -1,46 +0,0 @@
|
||||
"""
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
API to get current CSS theme for the client web UI
|
||||
"""
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
from flask import Blueprint, Response
|
||||
|
||||
import config
|
||||
from utils import readstatic
|
||||
|
||||
theme_blueprint = Blueprint('themes', __name__)
|
||||
|
||||
LIGHT_THEME_FILES = ['bulma-light.min.css', 'styles-light.css']
|
||||
DARK_THEME_FILES = ['bulma-dark.min.css', 'styles-dark.css']
|
||||
|
||||
def _load_from_files(file_list: list)->str:
|
||||
"""Loads multiple static dir files and returns them in combined string format (non-binary)"""
|
||||
combo_data = ''
|
||||
for f in file_list:
|
||||
combo_data += readstatic.read_static('www/shared/main/themes/' + f)
|
||||
return combo_data
|
||||
|
||||
@theme_blueprint.route('/gettheme', endpoint='getTheme')
|
||||
def get_theme_file()->Response:
|
||||
"""Returns the css theme data"""
|
||||
css: str
|
||||
theme = config.get('ui.theme', 'dark').lower()
|
||||
if theme == 'dark':
|
||||
css = _load_from_files(DARK_THEME_FILES)
|
||||
elif theme == 'light':
|
||||
css = _load_from_files(LIGHT_THEME_FILES)
|
||||
return Response(css, mimetype='text/css')
|
@ -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)
|
@ -3,26 +3,20 @@
|
||||
launch the api servers and communicator
|
||||
"""
|
||||
import os
|
||||
import queue
|
||||
from time import sleep
|
||||
import sys
|
||||
import platform
|
||||
import signal
|
||||
from threading import Thread
|
||||
|
||||
from stem.connection import IncorrectPassword
|
||||
import toomanyobjs
|
||||
import filenuke
|
||||
from deadsimplekv import DeadSimpleKV
|
||||
import psutil
|
||||
from ordered_set import OrderedSet
|
||||
|
||||
import config
|
||||
|
||||
import apiservers
|
||||
import logger
|
||||
from onionrplugins import onionrevents as events
|
||||
|
||||
from onionrutils import localcommand
|
||||
from utils import identifyhome
|
||||
import filepaths
|
||||
import onionrvalues
|
||||
@ -30,13 +24,11 @@ from onionrutils import cleanup
|
||||
from onionrcrypto import getourkeypair
|
||||
from onionrthreads import add_onionr_thread
|
||||
from blockdb.blockcleaner import clean_block_database
|
||||
import runtests
|
||||
from .. import version
|
||||
from .killdaemon import kill_daemon # noqa
|
||||
from .showlogo import show_logo
|
||||
import gossip
|
||||
|
||||
from setupkvvars import setup_kv
|
||||
"""
|
||||
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
|
||||
@ -53,11 +45,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
def _proper_shutdown():
|
||||
localcommand.local_command('shutdown')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _show_info_messages():
|
||||
version.version(verbosity=5, function=logger.info)
|
||||
logger.debug('Python version %s' % platform.python_version())
|
||||
@ -74,35 +61,13 @@ def daemon():
|
||||
"""Start Onionr's primary threads for communicator, API server, node, and LAN."""
|
||||
|
||||
def _handle_sig_term(signum, frame):
|
||||
pid = str(os.getpid())
|
||||
main_pid = localcommand.local_command('/getpid')
|
||||
#logger.info(main_pid, terminal=True)
|
||||
if main_pid and main_pid == pid:
|
||||
logger.info(
|
||||
f"Received sigterm, shutting down gracefully. PID: {pid}", terminal=True)
|
||||
localcommand.local_command('/shutdown')
|
||||
else:
|
||||
logger.info(
|
||||
f"Recieved sigterm in child process or fork, exiting. PID: {pid}")
|
||||
sys.exit(0)
|
||||
sys.exit(0)
|
||||
|
||||
with open(filepaths.pid_file, 'w') as f:
|
||||
f.write(str(os.getpid()))
|
||||
|
||||
signal.signal(signal.SIGTERM, _handle_sig_term)
|
||||
|
||||
# Create shared objects
|
||||
|
||||
shared_state = toomanyobjs.TooMany()
|
||||
|
||||
# Add DeadSimpleKV for quasi-global variables (ephemeral key-value)
|
||||
shared_state.get(DeadSimpleKV)
|
||||
|
||||
# Initialize the quasi-global variables
|
||||
setup_kv(shared_state.get(DeadSimpleKV))
|
||||
|
||||
# Init run time tester
|
||||
# (ensures Onionr is running right, for testing purposes)
|
||||
# Run time tests are not normally run
|
||||
shared_state.get(runtests.OnionrRunTestManager)
|
||||
|
||||
shared_state.share_object() # share the parent object to the threads
|
||||
|
||||
show_logo()
|
||||
|
||||
@ -123,12 +88,11 @@ def daemon():
|
||||
name='start_gossip_threads').start()
|
||||
|
||||
try:
|
||||
apiservers.private_api.start()
|
||||
events.event('shutdown', threaded=False)
|
||||
while True:
|
||||
sleep(60)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
|
||||
cleanup.delete_run_files()
|
||||
if security_level >= 2:
|
||||
filenuke.nuke.clean_tree(identifyhome.identify_home())
|
||||
|
@ -2,15 +2,11 @@
|
||||
|
||||
Gracefully stop Onionr daemon
|
||||
"""
|
||||
import sqlite3
|
||||
import os
|
||||
from signal import SIGTERM
|
||||
|
||||
from gevent import spawn
|
||||
|
||||
from onionrplugins import events
|
||||
from onionrutils import localcommand
|
||||
from filepaths import pid_file
|
||||
import logger
|
||||
import config
|
||||
"""
|
||||
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
|
||||
@ -29,27 +25,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
def kill_daemon():
|
||||
"""Shutdown the Onionr daemon (communicator)."""
|
||||
config.reload()
|
||||
try:
|
||||
with open(pid_file, 'r') as pid:
|
||||
os.kill(int(pid.read()), SIGTERM)
|
||||
except FileNotFoundError:
|
||||
logger.error("Daemon not running/pid file missing")
|
||||
logger.warn('Stopping the running daemon, if one exists...', timestamp=False,
|
||||
terminal=True)
|
||||
|
||||
# On platforms where we can, fork out to prevent locking
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid != 0:
|
||||
return
|
||||
except (AttributeError, OSError):
|
||||
pass
|
||||
|
||||
events.event('daemon_stop')
|
||||
try:
|
||||
spawn(
|
||||
localcommand.local_command,
|
||||
'/shutdown'
|
||||
).get(timeout=5)
|
||||
except sqlite3.OperationalError:
|
||||
pass
|
||||
|
||||
|
||||
kill_daemon.onionr_help = "Gracefully stops the " # type: ignore
|
||||
kill_daemon.onionr_help += "Onionr API servers" # type: ignore
|
@ -1,59 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Open the web interface properly into a web browser
|
||||
"""
|
||||
import webbrowser
|
||||
from time import sleep
|
||||
|
||||
import logger
|
||||
from onionrutils import getclientapiserver
|
||||
import config
|
||||
from onionrutils.localcommand import local_command
|
||||
|
||||
from .daemonlaunch import geturl
|
||||
"""
|
||||
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 get_url() -> str:
|
||||
"""Build UI URL string and return it."""
|
||||
return geturl.get_url(config)
|
||||
|
||||
|
||||
get_url.onionr_help = "Shows the Onionr " # type: ignore
|
||||
get_url.onionr_help += "web interface URL with API key" # type: ignore
|
||||
|
||||
|
||||
def open_home():
|
||||
"""Command to open web interface URL in default browser."""
|
||||
try:
|
||||
url = getclientapiserver.get_client_API_server()
|
||||
except FileNotFoundError:
|
||||
logger.error(
|
||||
'Onionr seems to not be running (could not get api host)',
|
||||
terminal=True)
|
||||
else:
|
||||
sleep(3) # Sleep a little to wait for web UI to init some vars it needs
|
||||
url = get_url()
|
||||
logger.info(
|
||||
'If Onionr does not open automatically, use this URL: ' + url,
|
||||
terminal=True)
|
||||
webbrowser.open_new_tab(url)
|
||||
|
||||
|
||||
open_home.onionr_help = "Opens the Onionr UI in the default " # type: ignore
|
||||
open_home.onionr_help += "browser. Node must be running." # type: ignore
|
@ -5,12 +5,8 @@ Sets CLI arguments for Onionr
|
||||
from typing import Callable
|
||||
|
||||
from .. import onionrstatistics, version, daemonlaunch
|
||||
from .. import openwebinterface
|
||||
from .. import pubkeymanager # commands to add or change id
|
||||
from .. import resetplugins # command to reinstall default plugins
|
||||
from .. import softreset # command to delete onionr blocks
|
||||
from .. import restartonionr # command to restart Onionr
|
||||
from .. import runtimetestcmd # cmd to execute the runtime integration tests
|
||||
|
||||
|
||||
import onionrexceptions
|
||||
@ -42,15 +38,9 @@ def get_arguments() -> dict:
|
||||
('version',): version.version,
|
||||
('start', 'daemon'): daemonlaunch.start,
|
||||
('stop', 'kill'): daemonlaunch.kill_daemon,
|
||||
('restart',): restartonionr.restart,
|
||||
('openhome', 'gui', 'openweb',
|
||||
'open-home', 'open-web'): openwebinterface.open_home,
|
||||
('get-url', 'url', 'get-web'): openwebinterface.get_url,
|
||||
('addid', 'add-id'): pubkeymanager.add_ID,
|
||||
('changeid', 'change-id'): pubkeymanager.change_ID,
|
||||
('resetplugins', 'reset-plugins'): resetplugins.reset,
|
||||
('soft-reset', 'softreset'): softreset.soft_reset,
|
||||
('runtime-test', 'runtimetest'): runtimetestcmd.do_runtime_test
|
||||
('resetplugins', 'reset-plugins'): resetplugins.reset
|
||||
}
|
||||
return args
|
||||
|
||||
|
@ -1,78 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Command to restart Onionr
|
||||
"""
|
||||
from threading import local
|
||||
import time
|
||||
import os
|
||||
import subprocess # nosec
|
||||
|
||||
from psutil import Process
|
||||
|
||||
import onionrvalues
|
||||
from onionrutils import cleanup
|
||||
from onionrutils import localcommand
|
||||
import logger
|
||||
import filepaths
|
||||
|
||||
from . import daemonlaunch
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
DEVNULL = subprocess.DEVNULL
|
||||
SCRIPT_NAME = os.path.dirname(os.path.realpath(
|
||||
__file__)) + f'/../../{onionrvalues.SCRIPT_NAME}'
|
||||
|
||||
|
||||
def restart():
|
||||
"""Tell the Onionr daemon to restart."""
|
||||
|
||||
logger.info('Restarting Onionr', terminal=True)
|
||||
|
||||
daemon_terminal = localcommand.local_command("getpid")
|
||||
terminal = None
|
||||
if daemon_terminal:
|
||||
terminal = Process(int(daemon_terminal)).terminal()
|
||||
else:
|
||||
terminal = Process().terminal()
|
||||
|
||||
# On platforms where we can, fork out to prevent locking
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid != 0:
|
||||
return
|
||||
except (AttributeError, OSError):
|
||||
logger.warn('Could not fork on restart')
|
||||
with open(filepaths.restarting_indicator, 'w') as f:
|
||||
f.write('t')
|
||||
daemonlaunch.kill_daemon()
|
||||
while localcommand.local_command('ping', max_wait=8) == 'pong!':
|
||||
time.sleep(0.3)
|
||||
time.sleep(15)
|
||||
while (os.path.exists(filepaths.private_API_host_file) or
|
||||
(os.path.exists(filepaths.daemon_mark_file))):
|
||||
time.sleep(1)
|
||||
|
||||
cleanup.delete_run_files()
|
||||
|
||||
with open(terminal, 'ab') as term:
|
||||
subprocess.Popen(
|
||||
[SCRIPT_NAME, 'start'],
|
||||
stdout=term,
|
||||
stdin=term,
|
||||
stderr=term)
|
||||
|
||||
|
||||
restart.onionr_help = 'Gracefully restart Onionr' # type: ignore
|
@ -1,37 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Command to tell daemon to do run time tests
|
||||
"""
|
||||
from gevent import spawn
|
||||
|
||||
from onionrutils import localcommand
|
||||
"""
|
||||
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 do_runtime_test():
|
||||
"""Send runtime test daemon queue command."""
|
||||
spawn(
|
||||
localcommand.local_command,
|
||||
f'daemon-event/test_runtime',
|
||||
post=True,
|
||||
is_json=True,
|
||||
post_data={},
|
||||
max_wait=300
|
||||
).get(300)
|
||||
|
||||
|
||||
do_runtime_test.onionr_help = "If Onionr is running, " # type: ignore
|
||||
do_runtime_test.onionr_help += "run runtime tests (check logs)" # type: ignore
|
@ -1,58 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Command to soft-reset Onionr (deletes blocks)
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from onionrutils import localcommand
|
||||
from coredb import dbfiles
|
||||
import filepaths
|
||||
from onionrplugins import onionrevents
|
||||
import logger
|
||||
"""
|
||||
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 _ignore_not_found_delete(path):
|
||||
try:
|
||||
os.remove(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def soft_reset():
|
||||
"""Command to soft reset Onionr home data.
|
||||
|
||||
Onionr must not be running
|
||||
"""
|
||||
if localcommand.local_command('/ping') == 'pong!':
|
||||
logger.warn('Cannot soft reset while Onionr is running',
|
||||
terminal=True)
|
||||
return
|
||||
path = filepaths.block_data_location
|
||||
shutil.rmtree(path)
|
||||
_ignore_not_found_delete(dbfiles.block_meta_db)
|
||||
_ignore_not_found_delete(filepaths.upload_list)
|
||||
_ignore_not_found_delete(filepaths.usage_file)
|
||||
onionrevents.event('softreset')
|
||||
logger.info("Soft reset Onionr", terminal=True)
|
||||
|
||||
|
||||
soft_reset.onionr_help = "Deletes Onionr blocks and their " # type: ignore
|
||||
soft_reset.onionr_help += "associated metadata, except for " # type: ignore
|
||||
soft_reset.onionr_help += "any exported block files. Does NOT " # type: ignore
|
||||
soft_reset.onionr_help += "delete data on " # type: ignore
|
||||
soft_reset.onionr_help += "other nodes in the network." # type: ignore
|
@ -1,6 +1,5 @@
|
||||
from . import safecompare, replayvalidation
|
||||
from . import safecompare
|
||||
from . import getpubfrompriv
|
||||
|
||||
replay_validator = replayvalidation.replay_timestamp_validation
|
||||
safe_compare = safecompare.safe_compare
|
||||
get_pub_key_from_priv = getpubfrompriv.get_pub_key_from_priv
|
||||
|
@ -1,3 +0,0 @@
|
||||
from onionrutils import epoch
|
||||
def replay_timestamp_validation(timestamp):
|
||||
return epoch.get_epoch() - int(timestamp) <= 2419200
|
@ -1,4 +1,6 @@
|
||||
import hmac
|
||||
|
||||
|
||||
def safe_compare(one, two):
|
||||
# Do encode here to avoid spawning core
|
||||
try:
|
||||
@ -10,3 +12,4 @@ def safe_compare(one, two):
|
||||
except AttributeError:
|
||||
pass
|
||||
return hmac.compare_digest(one, two)
|
||||
|
@ -1,38 +0,0 @@
|
||||
from .. import hashers
|
||||
import config, onionrproofs, logger
|
||||
import onionrexceptions
|
||||
def verify_POW(blockContent):
|
||||
'''
|
||||
Verifies the proof of work associated with a block
|
||||
'''
|
||||
retData = False
|
||||
|
||||
dataLen = len(blockContent)
|
||||
|
||||
try:
|
||||
blockContent = blockContent.encode()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
blockHash = hashers.sha3_hash(blockContent)
|
||||
try:
|
||||
blockHash = blockHash.decode() # bytes on some versions for some reason
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
difficulty = onionrproofs.getDifficultyForNewBlock(blockContent)
|
||||
|
||||
if difficulty < int(config.get('general.minimum_block_pow')):
|
||||
difficulty = int(config.get('general.minimum_block_pow'))
|
||||
mainHash = '0000000000000000000000000000000000000000000000000000000000000000'#nacl.hash.blake2b(nacl.utils.random()).decode()
|
||||
puzzle = mainHash[:difficulty]
|
||||
|
||||
if blockHash[:difficulty] == puzzle:
|
||||
# logger.debug('Validated block pow')
|
||||
retData = True
|
||||
else:
|
||||
logger.debug(f"Invalid token, bad proof for {blockHash} {puzzle}")
|
||||
raise onionrexceptions.InvalidProof('Proof for %s needs to be %s' % (blockHash, puzzle))
|
||||
|
||||
return retData
|
||||
|
@ -19,7 +19,7 @@
|
||||
'''
|
||||
|
||||
import onionrplugins, logger
|
||||
from onionrutils import localcommand
|
||||
|
||||
|
||||
class PluginAPI:
|
||||
def __init__(self, pluginapi):
|
||||
|
@ -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)
|
@ -38,3 +38,4 @@ def delete_run_files():
|
||||
_safe_remove(filepaths.daemon_mark_file)
|
||||
_safe_remove(filepaths.lock_file)
|
||||
_safe_remove(filepaths.gossip_server_socket_file)
|
||||
_safe_remove(filepaths.pid_file)
|
||||
|
@ -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,103 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
send a command to the local API server
|
||||
"""
|
||||
import urllib
|
||||
import time
|
||||
|
||||
import requests
|
||||
import deadsimplekv
|
||||
|
||||
import logger
|
||||
import config
|
||||
|
||||
from . import getclientapiserver
|
||||
import filepaths
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
config.reload()
|
||||
|
||||
cache = deadsimplekv.DeadSimpleKV(filepaths.cached_storage,
|
||||
refresh_seconds=1000)
|
||||
|
||||
|
||||
def get_hostname():
|
||||
hostname = ''
|
||||
waited = 0
|
||||
max_wait = 3
|
||||
while True:
|
||||
if cache.get('client_api') is None:
|
||||
try:
|
||||
hostname = getclientapiserver.get_client_API_server()
|
||||
except FileNotFoundError:
|
||||
hostname = False
|
||||
else:
|
||||
cache.put('hostname', hostname)
|
||||
cache.flush()
|
||||
else:
|
||||
hostname = cache.get('hostname')
|
||||
if hostname == '' or hostname is None:
|
||||
time.sleep(1)
|
||||
if waited == max_wait:
|
||||
return False
|
||||
else:
|
||||
return hostname
|
||||
|
||||
|
||||
def local_command(command, data='', silent=True, post=False,
|
||||
post_data={}, max_wait=20,
|
||||
is_json=False
|
||||
):
|
||||
"""Send a command to the local http API server, securely.
|
||||
Intended for local clients, DO NOT USE for remote peers."""
|
||||
hostname = get_hostname()
|
||||
# if the api host cannot be reached, return False
|
||||
if not hostname:
|
||||
return False
|
||||
|
||||
if data:
|
||||
data = '&data=' + urllib.parse.quote_plus(data)
|
||||
payload = 'http://%s/%s%s' % (hostname, command, data)
|
||||
if not config.get('client.webpassword'):
|
||||
config.reload()
|
||||
|
||||
try:
|
||||
if post:
|
||||
if is_json:
|
||||
ret_data = requests.post(
|
||||
payload,
|
||||
json=post_data,
|
||||
headers={'token': config.get('client.webpassword'),
|
||||
'Connection': 'close'},
|
||||
timeout=(max_wait, max_wait)).text
|
||||
else:
|
||||
ret_data = requests.post(
|
||||
payload,
|
||||
data=post_data,
|
||||
headers={'token': config.get('client.webpassword'),
|
||||
'Connection': 'close'},
|
||||
timeout=(max_wait, max_wait)).text
|
||||
else:
|
||||
ret_data = requests.get(payload,
|
||||
headers={'token':
|
||||
config.get('client.webpassword'),
|
||||
'Connection': 'close'},
|
||||
timeout=(max_wait, max_wait)).text
|
||||
except Exception as error:
|
||||
if not silent:
|
||||
logger.error('Failed to make local request (command: %s):%s' %
|
||||
(command, error), terminal=True)
|
||||
ret_data = False
|
||||
return ret_data
|
@ -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
|
@ -23,54 +23,24 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
DENIABLE_PEER_ADDRESS = "OVPCZLOXD6DC5JHX4EQ3PSOGAZ3T24F75HQLIUZSDSMYPEOXCPFA"
|
||||
PASSWORD_LENGTH = 25
|
||||
ONIONR_TAGLINE = 'Private P2P Communication - GPLv3 - https://Onionr.net'
|
||||
ONIONR_VERSION = '8.0.2'
|
||||
ONIONR_VERSION = '9.0.0'
|
||||
ONIONR_VERSION_CODENAME = 'Taraxacum'
|
||||
ONIONR_VERSION_TUPLE = tuple(ONIONR_VERSION.split('.')) # (MAJOR, MINOR, VERSION)
|
||||
API_VERSION = '2' # increments of 1; only change when something fundamental about how the API works changes. This way other nodes know how to communicate without learning too much information about you.
|
||||
MIN_PY_VERSION = 7 # min version of 7 so we can take advantage of non-cyclic type hints
|
||||
DEVELOPMENT_MODE = False
|
||||
IS_QUBES = False
|
||||
"""limit type length for a block (soft enforced, ignored if invalid but block still stored)."""
|
||||
MAX_BLOCK_TYPE_LENGTH = 15
|
||||
"""limit clock timestamp for new blocks to be skewed in the future in seconds,
|
||||
2 minutes to allow plenty of time for slow block insertion and slight clock inaccuracies"""
|
||||
MAX_BLOCK_CLOCK_SKEW = 120
|
||||
|
||||
"""Onionr user IDs are ed25519 keys, which are always 32 bytes in length"""
|
||||
MAIN_PUBLIC_KEY_SIZE = 32
|
||||
ORIG_RUN_DIR_ENV_VAR = 'ORIG_ONIONR_RUN_DIR'
|
||||
|
||||
DATABASE_LOCK_TIMEOUT = 60
|
||||
|
||||
# Block creation anonymization requirements
|
||||
MIN_BLOCK_UPLOAD_PEER_PERCENT = 0.1
|
||||
|
||||
WSGI_SERVER_REQUEST_TIMEOUT_SECS = 120
|
||||
|
||||
MAX_NEW_PEER_QUEUE = 1000
|
||||
|
||||
BLOCK_EXPORT_FILE_EXT = '.onionr'
|
||||
|
||||
# Begin OnionrValues migrated values
|
||||
|
||||
"""30 days is plenty of time for someone to decide to renew a block"""
|
||||
DEFAULT_EXPIRE = 2678400
|
||||
# Metadata header section length limits, in bytes
|
||||
BLOCK_METADATA_LENGTHS = {'meta': 1000, 'sig': 200, 'signer': 200, 'time': 10,
|
||||
'n': 1000, 'c': 1000, 'encryptType': 4, 'expire': 14}
|
||||
|
||||
# Pool Eligibility Max Age
|
||||
BLOCK_POOL_MAX_AGE = 300
|
||||
|
||||
"""Public key that signs MOTD messages shown in the web UI"""
|
||||
MOTD_SIGN_KEY = "TRH763JURNY47QPBTTQ4LLPYCYQK6Q5YA33R6GANKZK5C5DKCIGQ"
|
||||
|
||||
"""Public key that signs update notifications."""
|
||||
UPDATE_SIGN_KEY = "TRH763JURNY47QPBTTQ4LLPYCYQK6Q5YA33R6GANKZK5C5DKCIGQ"
|
||||
|
||||
|
||||
if os.path.exists(filepaths.daemon_mark_file):
|
||||
SCRIPT_NAME = 'start-daemon.sh'
|
||||
else:
|
||||
SCRIPT_NAME = 'onionr.sh'
|
||||
if 'qubes' in platform.release().lower():
|
||||
IS_QUBES = True
|
||||
|
@ -1,77 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Test Onionr as it is running
|
||||
"""
|
||||
import os
|
||||
from secrets import SystemRandom
|
||||
|
||||
import logger
|
||||
from onionrutils import epoch
|
||||
|
||||
from . import uicheck
|
||||
from .webpasstest import webpass_test
|
||||
from .osver import test_os_ver_endpoint
|
||||
from .dnsrebindingtest import test_dns_rebinding
|
||||
"""
|
||||
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/>.
|
||||
"""
|
||||
|
||||
RUN_TESTS = [uicheck.check_ui,
|
||||
webpass_test,
|
||||
test_os_ver_endpoint,
|
||||
]
|
||||
|
||||
SUCCESS_FILE = os.path.dirname(os.path.realpath(__file__)) + '/../../tests/runtime-result.txt'
|
||||
|
||||
|
||||
class OnionrRunTestManager:
|
||||
def __init__(self):
|
||||
self.success: bool = True
|
||||
self.run_date: int = 0
|
||||
|
||||
def run_tests(self):
|
||||
tests = list(RUN_TESTS)
|
||||
SystemRandom().shuffle(tests)
|
||||
cur_time = epoch.get_epoch()
|
||||
logger.info(f"Doing runtime tests at {cur_time}")
|
||||
|
||||
try:
|
||||
os.remove(SUCCESS_FILE)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
done_count: int = 0
|
||||
total_to_do: int = len(tests)
|
||||
|
||||
try:
|
||||
for i in tests:
|
||||
last = i
|
||||
logger.info("[RUNTIME TEST] " + last.__name__ + " started",
|
||||
terminal=True, timestamp=True)
|
||||
i(self)
|
||||
done_count += 1
|
||||
logger.info("[RUNTIME TEST] " + last.__name__ +
|
||||
f" passed {done_count}/{total_to_do}",
|
||||
terminal=True, timestamp=True)
|
||||
except (ValueError, AttributeError):
|
||||
logger.error(last.__name__ + ' failed assertions', terminal=True)
|
||||
except Exception as e:
|
||||
logger.error(last.__name__ + ' failed with non-asserting exception')
|
||||
logger.error(str(e))
|
||||
else:
|
||||
ep = str(epoch.get_epoch())
|
||||
logger.info(f'All runtime tests passed at {ep}', terminal=True)
|
||||
with open(SUCCESS_FILE, 'w') as f:
|
||||
f.write(ep)
|
||||
|
@ -1,46 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Test apis for dns rebinding
|
||||
"""
|
||||
import config
|
||||
import requests
|
||||
from filepaths import private_API_host_file, public_API_host_file
|
||||
import logger
|
||||
"""
|
||||
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 test_dns_rebinding(test_manager):
|
||||
f = ''
|
||||
with open(private_API_host_file, 'r') as f:
|
||||
host = f.read()
|
||||
private_api_port = config.get('client.client.port')
|
||||
|
||||
if requests.get(f'http://{host}:{private_api_port}/ping', headers={'host': 'example.com'}) == 'pong!':
|
||||
raise ValueError('DNS rebinding failed')
|
||||
logger.info('It is normal to see 403 errors right now', terminal=True)
|
||||
|
||||
if config.get('general.security_level', 0) > 0 or not config.get('transports.tor', True):
|
||||
return
|
||||
public_api_port = config.get('client.public.port')
|
||||
f = ''
|
||||
with open(public_API_host_file, 'r') as f:
|
||||
host = f.read()
|
||||
|
||||
if requests.get(f'http://{host}:{public_api_port}/ping', headers={'host': 'example.com'}) == 'pong!':
|
||||
raise ValueError('DNS rebinding failed')
|
||||
logger.info('It is normal to see 403 errors right now', terminal=True)
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
import platform
|
||||
|
||||
from onionrutils import localcommand
|
||||
|
||||
|
||||
def test_os_ver_endpoint(test_manager):
|
||||
if localcommand.local_command('os') != platform.system().lower():
|
||||
raise ValueError('could not get proper os platform from endpoint /os')
|
@ -1,9 +0,0 @@
|
||||
from onionrutils import localcommand
|
||||
def check_ui(test_manager):
|
||||
endpoints = ['/']
|
||||
for point in endpoints:
|
||||
result = localcommand.local_command(point)
|
||||
if not result: raise ValueError
|
||||
result = result.lower()
|
||||
if 'script' not in result:
|
||||
raise ValueError(f'uicheck failed on {point}')
|
@ -1,11 +0,0 @@
|
||||
import requests
|
||||
|
||||
from onionrutils import localcommand
|
||||
|
||||
|
||||
def webpass_test(test_manager):
|
||||
if requests.get('http://' + localcommand.get_hostname() + '/ping') == \
|
||||
'pong!':
|
||||
raise ValueError
|
||||
if localcommand.local_command('ping') != 'pong!':
|
||||
raise ValueError('Could not ping with normal localcommand in webpasstest')
|
@ -1,34 +0,0 @@
|
||||
"""Onionr - Private P2P Communication.
|
||||
|
||||
Initialize singleton deadsimplekv pseudo globals
|
||||
"""
|
||||
import queue
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from onionrutils import epoch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from deadsimplekv import DeadSimpleKV
|
||||
"""
|
||||
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 setup_kv(shared_vars: 'DeadSimpleKV'):
|
||||
"""Init initial pseudo-globals."""
|
||||
shared_vars.put('shutdown', False)
|
||||
shared_vars.put('generating_blocks', [])
|
||||
shared_vars.put('startTime', epoch.get_epoch())
|
||||
shared_vars.put('isOnline', True)
|
@ -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
|
@ -13,4 +13,4 @@ echo "Future Onionr commands will use your set or default Onionr home directory,
|
||||
echo "Ultimately, a live boot operating system such as Tails or Debian would be better for you to use."
|
||||
$(dirname $0)/onionr.sh start & disown
|
||||
sleep 2
|
||||
$(dirname $0)/onionr.sh open-home
|
||||
#$(dirname $0)/onionr.sh open-home
|
||||
|
@ -1 +0,0 @@
|
||||
https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/robots.txt,http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/robots.txt,http://rurcblzhmdk22kttfkel2zduhyu3r6to7knyc7wiorzrx5gw4c3lftad.onion/
|
@ -19,7 +19,6 @@ import onionrblocks
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
|
||||
# import after path insert
|
||||
from onionrutils.localcommand import local_command
|
||||
|
||||
"""
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -43,12 +42,6 @@ PLUGIN_VERSION = '0.0.0'
|
||||
|
||||
def on_blocktest_cmd(api, data=None):
|
||||
bl = onionrblocks.create_anonvdf_block(input("Enter a message:").encode('utf-8'), b"tst", 3600)
|
||||
logger.info(
|
||||
local_command(
|
||||
'/addvdfblock',
|
||||
post_data=bl.id + bl.raw,
|
||||
silent=False, post=True),
|
||||
terminal=True)
|
||||
|
||||
|
||||
def on_printtest_cmd(api, data=None):
|
||||
|
@ -57,7 +57,10 @@ def on_bootstrap(api, data):
|
||||
|
||||
while not config.get('tor.transport_address'):
|
||||
sleep(1)
|
||||
config.reload()
|
||||
try:
|
||||
config.reload()
|
||||
except Exception:
|
||||
logger.error(traceback.format_exc(), terminal=True)
|
||||
|
||||
socks_address, socks_port = get_socks()[0]
|
||||
|
||||
|
@ -1,5 +1,15 @@
|
||||
import socks
|
||||
|
||||
from gossip.peerset import gossip_peer_set
|
||||
import logger
|
||||
|
||||
class HandleRevc:
|
||||
def __init__(self, sock):
|
||||
self.sock_recv = sock.recv
|
||||
|
||||
def recv(self, *args, **kwargs):
|
||||
return self.sock_recv(*args, **kwargs)
|
||||
|
||||
|
||||
class TorPeer:
|
||||
|
||||
@ -18,7 +28,18 @@ class TorPeer:
|
||||
s = socks.socksocket()
|
||||
s.set_proxy(socks.SOCKS4, self.socks_host, self.socks_port, rdns=True)
|
||||
s.settimeout(connect_timeout)
|
||||
s.connect((self.onion_address, 80))
|
||||
try:
|
||||
s.connect((self.onion_address, 80))
|
||||
except socks.GeneralProxyError:
|
||||
try:
|
||||
gossip_peer_set.remove(self)
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
logger.debug(f"Could not create socket to peer {self.transport_address}", terminal=True)
|
||||
raise
|
||||
mock_recv = HandleRevc(s)
|
||||
s.recv = mock_recv.recv
|
||||
return s
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -43,7 +43,7 @@
|
||||
"minimum_score": -100
|
||||
},
|
||||
"plugins": {
|
||||
"disabled": [],
|
||||
"disabled": ["unixtransport"],
|
||||
"enabled": []
|
||||
},
|
||||
"statistics": {
|
||||
|
Binary file not shown.
@ -1,96 +0,0 @@
|
||||
/*
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This file handles the UI for managing friends/contacts
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
friendListDisplay = document.getElementById('friendList')
|
||||
addForm = document.getElementById('addFriend')
|
||||
|
||||
function removeFriend(pubkey){
|
||||
post_to_url('/friends/remove/' + pubkey, {'token': webpass})
|
||||
}
|
||||
|
||||
addForm.onsubmit = function(){
|
||||
var friend = document.getElementsByName('addKey')[0]
|
||||
var alias = document.getElementsByName('data')[0]
|
||||
if (alias.value.toLowerCase().trim() == 'anonymous'){
|
||||
PNotify.error({
|
||||
text: "Anonymous is a reserved alias name"
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
fetch('/friends/add/' + friend.value, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"token": webpass
|
||||
}}).then(function(data) {
|
||||
if (alias.value.trim().length > 0){
|
||||
post_to_url('/friends/setinfo/' + friend.value + '/name', {'data': alias.value, 'token': webpass})
|
||||
}
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fetch('/friends/list', {
|
||||
headers: {
|
||||
"token": webpass
|
||||
}})
|
||||
.then((resp) => resp.json()) // Transform the data into json
|
||||
.then(function(resp) {
|
||||
var keys = [];
|
||||
for(var k in resp) keys.push(k);
|
||||
console.log(keys)
|
||||
|
||||
if (keys.length == 0){
|
||||
friendListDisplay.innerText = "None yet :("
|
||||
}
|
||||
for (var i = 0; i < keys.length; i++){
|
||||
var peer = keys[i]
|
||||
var name = resp[keys[i]]['name']
|
||||
if (name === null || name === ''){
|
||||
name = peer
|
||||
}
|
||||
var entry = document.createElement('div')
|
||||
var nameText = document.createElement('input')
|
||||
removeButton = document.createElement('button')
|
||||
removeButton.classList.add('friendRemove')
|
||||
removeButton.classList.add('button', 'is-danger')
|
||||
entry.setAttribute('data-pubkey', peer)
|
||||
removeButton.innerText = 'X'
|
||||
nameText.value = name
|
||||
nameText.readOnly = true
|
||||
nameText.style.fontStyle = "italic"
|
||||
entry.style.paddingTop = '8px'
|
||||
entry.appendChild(removeButton)
|
||||
entry.appendChild(nameText)
|
||||
friendListDisplay.appendChild(entry)
|
||||
}
|
||||
// If friend delete buttons are pressed
|
||||
|
||||
var friendRemoveBtns = document.getElementsByClassName('friendRemove')
|
||||
|
||||
for (var x = 0; x < friendRemoveBtns.length; x++){
|
||||
var friendKey = friendRemoveBtns[x].parentElement.getAttribute('data-pubkey')
|
||||
friendRemoveBtns[x].onclick = function(){
|
||||
removeFriend(friendKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})
|
@ -1,160 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>
|
||||
Friends
|
||||
</title>
|
||||
<link rel='shortcut icon' type='image/ico' href='/shared/images/favicon.ico'>
|
||||
<link rel="stylesheet" href="/shared/fontawesome-free-5.10.2/css/all.min.css">
|
||||
<link rel='stylesheet' href='/shared/main/PNotifyBrightTheme.css'>
|
||||
<link rel="stylesheet" href="/gettheme">
|
||||
<link rel='stylesheet' href='/friends/style.css'>
|
||||
<script defer src="/shared/node_modules/pnotify/dist/iife/PNotify.js"></script>
|
||||
<script defer src="/shared/node_modules/pnotify/dist/iife/PNotifyButtons.js"></script>
|
||||
<script defer src="/shared/misc.js"></script>
|
||||
<script defer src="/friends/friends.js"></script>
|
||||
<script defer src="/shared/navbar.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar is-dark" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item idLink" href="/">
|
||||
<img src="/shared/images/favicon.ico" class="navbarLogo">
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
|
||||
data-target="navbarBasic">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasic" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item idLink" href="/mail/">Mail</a>
|
||||
<a class="navbar-item idLink" href="/friends/">Friends</a>
|
||||
<a class="navbar-item idLink" href="/board/">Circles</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!--Hero (Dark Section)-->
|
||||
<section class="hero is-small is-dark">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h1 class="title">
|
||||
Friends
|
||||
</h1>
|
||||
<h2 class="subtitle">
|
||||
Manage your friend list
|
||||
</h2>
|
||||
</div>
|
||||
<div class="column is-7">
|
||||
<div class="field">
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
<a class="button is-static">
|
||||
<i class="fas fa-fingerprint"></i>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control is-expanded">
|
||||
<input id="myPub" class="input myPub" type="text" readonly>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a id="myPubCopy" class="button is-primary">Copy</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<br>
|
||||
|
||||
<!--Start Content-->
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<!--Add Friend-->
|
||||
<div class="column is-one-third">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
Add Friend
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<form id='addFriend' action='/' method='POST'>
|
||||
<div class="field">
|
||||
<label class="label">Friend ID</label>
|
||||
<p class="control is-expanded">
|
||||
<input id="" class="input" type="text" name='addKey' placeholder='Public Key/ID'
|
||||
minlength="52" maxlength="500" required>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Alias</label>
|
||||
<p class="control is-expanded">
|
||||
<input id="" class="input" type="text" name='data' placeholder='Name' required>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item">
|
||||
<button class="button is-success" type='submit'>Add Friend</button>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<!--Friend List-->
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
Friend List
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<div id='friendList'>
|
||||
<!--Friend list is appended here-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--Template markup for friend list-->
|
||||
<template id="friendTemplate">
|
||||
<div class="box">
|
||||
<div class="columns">
|
||||
<div class="column is-narrow" id='friendName'>
|
||||
Name
|
||||
</div>
|
||||
<div class="column" id='friendPubkey'>
|
||||
Public Key
|
||||
</div>
|
||||
<div class="column is-narrow friendRemove" id='defriend'>
|
||||
<a class="">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,4 +0,0 @@
|
||||
#friendList button{
|
||||
display: inline;
|
||||
margin-right: 10px;
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
let skipConsentBtns = function(){
|
||||
var skipBtn = document.getElementsByClassName('skipToConsent')[0]
|
||||
var sections = document.getElementsByClassName('step-item')
|
||||
var content = document.getElementsByClassName('step-content')
|
||||
skipBtn.onclick = function(e){
|
||||
e.preventDefault()
|
||||
for (el of document.querySelectorAll('.steps-action a')){
|
||||
el.classList.add('is-hidden')
|
||||
}
|
||||
for (el of content){
|
||||
el.classList.remove('is-active')
|
||||
var last = el
|
||||
}
|
||||
last.classList.add('is-active')
|
||||
for (el = 0; el <= sections.length; el++){
|
||||
sections[el].classList.add('is-completed')
|
||||
sections[el].classList.remove('is-active')
|
||||
var last = sections[el]
|
||||
}
|
||||
last.classList.add('is-active')
|
||||
|
||||
//is-completed
|
||||
}
|
||||
}()
|
@ -1,13 +0,0 @@
|
||||
.donateHeader{
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.donateFinished{
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.donateBody p{
|
||||
padding-top: 1em;
|
||||
text-align: justify;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
<b class='donateHeader'>Donate</b>
|
||||
|
||||
<br><br>
|
||||
|
||||
<p>Onionr is a volunteer-driven project, and infrastructure is paid for out of pocket.
|
||||
</p>
|
||||
<p>
|
||||
Please donate the price of a cup of coffee to sustain development.
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
Paypal/Card: <a href="https://ko-fi.com/beardogkf" target="_blank" rel="noopener">Ko-fi</a>
|
||||
<br>
|
||||
Bitcoin: <a href="bitcoin:1onion55FXzm6h8KQw3zFw2igpHcV7LPq">1onion55FXzm6h8KQw3zFw2igpHcV7LPq</a>
|
||||
|
||||
<button class="button donateFinished is-success">Done</button>
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Handles onboarding donations for Onionr
|
||||
|
||||
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/>
|
||||
*/
|
||||
|
||||
|
||||
let donateFinishedButtons = document.getElementsByClassName('donateFinished')
|
||||
configInfo = {}
|
||||
|
||||
let openDonateModal = function(newConfigInfo){
|
||||
fetch('donate-modal.html')
|
||||
.then((resp) => resp.text())
|
||||
.then(function(resp) {
|
||||
document.getElementsByClassName('donateModal')[0].classList.add('is-active')
|
||||
|
||||
// Load the donate modal html and display it
|
||||
let donateBody = document.getElementsByClassName('donateBody')[0]
|
||||
|
||||
donateBody.innerHTML = resp
|
||||
|
||||
let donateFinishedButton = document.getElementsByClassName('donateFinished')[0]
|
||||
|
||||
for (i = 0; i < donateFinishedButtons.length; i++){
|
||||
donateFinishedButtons[i].onclick = function(){
|
||||
document.getElementsByClassName('donateModal')[0].classList.remove('is-active')
|
||||
sendConfig(configInfo)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
@ -1,291 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!--Mobile responsive-->
|
||||
<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">
|
||||
<link rel="stylesheet" href="/private/main.css">
|
||||
<link rel="stylesheet" href="/shared/fontawesome-free-5.10.2/css/all.min.css">
|
||||
<link rel="stylesheet" href="/gettheme">
|
||||
<link rel="stylesheet" href="/shared/bulma-steps.min.css">
|
||||
<link rel="stylesheet" href="/shared/bulma-tooltip.min.css">
|
||||
<link rel="stylesheet" href="donate-modal.css">
|
||||
<link rel="stylesheet" href="onboarding.css">
|
||||
|
||||
<script defer src="/shared/loadabout.js"></script>
|
||||
<script defer src="/shared/misc.js"></script>
|
||||
<script defer src="/private/js/console.js"></script>
|
||||
<script defer src="donate.js"></script>
|
||||
<script defer src="onboarding.js"></script>
|
||||
<script defer src="/shared/panel.js"></script>
|
||||
<script defer src="/shared/bulma-steps.min.js"></script>
|
||||
<script defer src="consentskip.js"></script>
|
||||
<script>alert("Content security policy appears to not be working. Your browser security is weak!")</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="shutdownNotice" class="overlay">
|
||||
<div>
|
||||
<p>Your node will shutdown. Thank you for using Onionr.</p>
|
||||
<p>If you are using random bind IPs (default in non dev mode), Onionr will have a different URL next time.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--Hero (Dark Bar)-->
|
||||
<section class="hero is-small is-dark">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="image is-128x128">
|
||||
<img src="/shared/images/onionr-text.png">
|
||||
</div>
|
||||
<h2 class="subtitle">
|
||||
Private Decentralized Communication
|
||||
</h2>
|
||||
</div>
|
||||
<div class="column is-7">
|
||||
<div class="field is-grouped is-pulled-right">
|
||||
<p class="control">
|
||||
<a class="button is-danger is-outlined" id="shutdownNode">
|
||||
Shutdown
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="modal donateModal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<button class="closeAboutModal delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body donateBody">
|
||||
Loading... <i class="fas fa-spinner fa-spin"></i>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="onboarding">
|
||||
<noscript><h1>Unfortunately, this requires JavaScript. Don't worry, all scripts are open source and locally loaded.</h1></noscript>
|
||||
<form method="post" id="onboardingForm">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="steps" id="stepsDemo">
|
||||
<div class="step-item is-active">
|
||||
<div class="step-marker">1</div>
|
||||
<div class="step-details">
|
||||
<p class="step-title">Welcome</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item">
|
||||
<div class="step-marker">2</div>
|
||||
<div class="step-details">
|
||||
<p class="step-title">Security</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item">
|
||||
<div class="step-marker">3</div>
|
||||
<div class="step-details">
|
||||
<p class="step-title">Plugins</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item">
|
||||
<div class="step-marker">4</div>
|
||||
<div class="step-details">
|
||||
<p class="step-title">Options</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item">
|
||||
<div class="step-marker">5</div>
|
||||
<div class="step-details">
|
||||
<p class="step-title">Consent</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="steps-content">
|
||||
<div class="step-content has-text-justified is-active">
|
||||
<div class="box">
|
||||
<p>The default settings are intended to be safe for most users.</p>
|
||||
<p>Use the button below to skip to the end.</p>
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<button class="button is-primary formBtn skipToConsent">Use default setup</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item has-text-danger">
|
||||
<small class="is-danger">Those targeted by powerful actors (governments, organized crime,
|
||||
<span data-tooltip="advanced persistent threats">APTs</span>) should press next instead.</small>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-content has-text-justified">
|
||||
<div class="box">
|
||||
<p>Configure Onionr according to one's <a href="https://ssd.eff.org/en/module/your-security-plan" target="_blank" rel="noopener">threat model</a></p>
|
||||
<p>I am concerned about the following:</p>
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item"><i class="icon fas fa-eye" for="state"></i> <input type="checkbox" name="state" checked></div>
|
||||
<label for="state">Mass surveillance</label>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<i class="icon fas fa-university" for="stateTarget"></i> <input type="checkbox" name="stateTarget">
|
||||
</div>
|
||||
<label for="stateTarget">State actor</label>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<i class="icon fas fa-binoculars" for="local"></i> <input type="checkbox" name="local"> <label for="local">Nearby threat</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-content has-text-justified">
|
||||
<div class="box">
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<i class="icon fas fa-envelope" for="useMail"></i> <input type="checkbox" name="useMail" checked> <label for="useMail">Use OnionrMail</label>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<i class="icon fas fa-comments" for="useCircles"></i> <input type="checkbox" name="useCircles" checked> <label for="useCircles">Use Circles (message board system)</label>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-content has-text-justified">
|
||||
<div class="box">
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<i class="icon fas fa-palette" for="useDarkTheme"></i> <input type="checkbox" name="useDarkTheme" checked> <label for="useDarkTheme">Use Dark Theme</label>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<i class="icon fas fa-dollar-sign" for="donate"></i> <input type="checkbox" name="donate"> <label for="donate">Donate the price of a coffee</label>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<i class="icon fas fa-microchip" for="optimize"></i> <input type="checkbox" name="optimize"> <label for="optimize">Optimize CPU</label>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-content has-text-justified">
|
||||
<div class="box">
|
||||
<p>I want to contribute resources to the following...</p>
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<i class="icon fas fa-book-open" for="networkContributionPlain"></i> <input checked type="checkbox" name="networkContributionPlain"> <label for="networkContributionPlain">Sharing and storing plaintext data</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="box">
|
||||
<nav class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<i class="icon fas fa-network-wired" for="networkContribution"></i> <input checked type="checkbox" name="networkContribution"> <label for="networkContribution">Sharing and storing encrypted data</label>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" class="button is-primary" value="Complete Setup">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="steps-actions">
|
||||
<div class="steps-action">
|
||||
<a data-nav="previous" class="button is-light">Previous</a>
|
||||
</div>
|
||||
<div class="steps-action">
|
||||
<a data-nav="next" class="button is-light">Next</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,48 +0,0 @@
|
||||
img{
|
||||
vertical-align: middle;
|
||||
}
|
||||
.navbarLogo{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.onboarding{
|
||||
padding-left: 5%;
|
||||
text-align: justify;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.icon{
|
||||
padding-right: 1em;
|
||||
}
|
||||
form{
|
||||
margin-left: 2%;
|
||||
}
|
||||
|
||||
|
||||
noscript{
|
||||
font-size: 2em;
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
p{
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.formBtn{
|
||||
margin-top: 1em;
|
||||
}
|
||||
.steps-content{
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.step-content .box{
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.step-content .box span{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.steps-content input[type='checkbox'] {
|
||||
margin-right: 10px;
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Handles onboarding for Onionr
|
||||
|
||||
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/>
|
||||
*/
|
||||
|
||||
fetch('/getnewkeys', {
|
||||
headers: {
|
||||
"token": webpass
|
||||
}})
|
||||
.then((resp) => resp.text())
|
||||
.then(function(resp) {
|
||||
keys = resp.split('')
|
||||
})
|
||||
|
||||
function getCheckValue(elName){
|
||||
return document.getElementsByName(elName)[0].checked
|
||||
}
|
||||
|
||||
function sendConfig(configInfo){
|
||||
fetch('/setonboarding', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"token": webpass,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({configInfo})
|
||||
}).then(function(data) {
|
||||
window.location.href = window.location.origin + '/' + window.location.hash
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
document.getElementById('onboardingForm').onsubmit = function(e){
|
||||
submitInfo = {}
|
||||
submitInfo.massSurveil = getCheckValue('state')
|
||||
submitInfo.stateTarget = getCheckValue('stateTarget')
|
||||
submitInfo.localThreat = getCheckValue('local')
|
||||
submitInfo.networkContrib = getCheckValue('networkContribution')
|
||||
submitInfo.plainContrib = getCheckValue('networkContributionPlain')
|
||||
submitInfo.donate = getCheckValue('donate')
|
||||
//submitInfo.deterministic = getCheckValue('useDeterministic')
|
||||
submitInfo.mail = getCheckValue('useMail')
|
||||
submitInfo.circles = getCheckValue('useCircles')
|
||||
submitInfo.useDark = getCheckValue('useDarkTheme')
|
||||
|
||||
if (submitInfo.donate){
|
||||
openDonateModal(submitInfo)
|
||||
return false
|
||||
}
|
||||
|
||||
sendConfig(submitInfo)
|
||||
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
/* Fix label clicking since bulma is weird */
|
||||
|
||||
let labelClickFix = function(labels) {
|
||||
for (i = 0; i < labels.length; i++){
|
||||
labels[i].onclick = function(event){
|
||||
document.getElementsByName(event.target.getAttribute("for"))[0].checked ^= 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
let setupLabelFix = function(){
|
||||
var labels = document.getElementsByTagName('label')
|
||||
var icons = document.getElementsByTagName('i')
|
||||
labelClickFix(labels)
|
||||
labelClickFix(icons)
|
||||
}
|
||||
setupLabelFix()
|
Binary file not shown.
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.1 KiB |
@ -1,373 +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">
|
||||
<link rel="stylesheet" href="/private/main.css">
|
||||
<link rel="stylesheet" href="/shared/fontawesome-free-5.10.2/css/all.min.css">
|
||||
<link rel="stylesheet" href="/shared/main/PNotifyBrightTheme.css">
|
||||
<link rel="stylesheet" href="/shared/bulma-quickview.min.css">
|
||||
<link rel="stylesheet" href="/shared/bulma-tooltip.min.css">
|
||||
<link rel="stylesheet" href="/gettheme">
|
||||
<link rel="stylesheet" href="/shared/sidebar/sidebar.css">
|
||||
<script defer src="/shared/node_modules/pnotify/dist/iife/PNotify.js"></script>
|
||||
<script defer src="/shared/node_modules/pnotify/dist/iife/PNotifyButtons.js"></script>
|
||||
<script defer src="/shared/bulma-quickview.js"></script>
|
||||
<script defer src="/shared/eventsource.js"></script>
|
||||
<script defer src="/shared/main/particles.js"></script>
|
||||
<script defer src="/shared/loadabout.js"></script>
|
||||
<script defer src="/shared/misc.js"></script>
|
||||
<script defer src="/shared/getos.js"></script>
|
||||
<script defer src="/shared/main/stats.js"></script>
|
||||
<script defer src="/shared/main/recent.js"></script>
|
||||
<script defer src="/shared/main/torstats.js"></script>
|
||||
<script defer src="/shared/panel.js"></script>
|
||||
<script defer src="/shared/configeditor.js"></script>
|
||||
<script defer src="/shared/sites.js"></script>
|
||||
<script defer src="/shared/main/apicheck.js"></script>
|
||||
<script defer src="/private/js/console.js"></script>
|
||||
<script defer src="/private/js/motd.js"></script>
|
||||
<script defer src="/shared/navbar.js"></script>
|
||||
<script defer src="/shared/sidebar/sidebar.js"></script>
|
||||
<script defer src="/shared/main/loadTransport.js"></script>
|
||||
<script>alert("Content security policy appears to not be working. Your browser security is weak!")</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="shutdownNotice" class="overlay">
|
||||
<div>
|
||||
<p>Your node will shutdown. Thank you for using Onionr.</p>
|
||||
<p>If you are using random bind IPs (default in non dev mode), Onionr will have a different URL next time.</p>
|
||||
</div>
|
||||
</div>
|
||||
<span id="sidebarContainer"></span>
|
||||
<nav class="navbar is-dark" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item idLink" href="/">
|
||||
<img src="/shared/images/favicon.ico" class="navbarLogo">
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
|
||||
data-target="navbarBasic">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasic" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item idLink" href="/mail/">Mail</a>
|
||||
<a class="navbar-item idLink" href="/friends/">Friends</a>
|
||||
<a class="navbar-item idLink" href="/board/">Circles</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<a class="navbar-item idLink aboutLink">About</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!--Hero (Dark Bar)-->
|
||||
<section class="hero is-small is-dark">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h1 class="title">
|
||||
Onionr
|
||||
</h1>
|
||||
<h2 class="subtitle">
|
||||
Private Decentralized Communication
|
||||
</h2>
|
||||
</div>
|
||||
<div class="column is-7">
|
||||
<div class="field is-grouped is-grouped-centered">
|
||||
<p class="control">
|
||||
<a class="button is-danger is-outlined" id="shutdownNode">
|
||||
Shutdown
|
||||
</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-warning is-outlined" id="restartNode">
|
||||
Restart
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
<!--Start of content-->
|
||||
<div class="container mainCont">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<!--Onionr Card-->
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<span class="card-header-icon">
|
||||
<span class="icon">
|
||||
<i class="fas fa-link"></i>
|
||||
</span>
|
||||
</span>
|
||||
<p class="card-header-title">
|
||||
Onionr Sites
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<div class="field" data-tooltip="Site public key or hash (not onion)">
|
||||
<label class="label">Open Site</label>
|
||||
<div class="field has-addons">
|
||||
<p class="control is-expanded">
|
||||
<input class="input" type="text" id="siteViewer" placeholder="Site ID">
|
||||
</p>
|
||||
<p class="control">
|
||||
<a id="openSite" class="button is-info">Open</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card motdCard">
|
||||
<header class="card-header">
|
||||
<span class="card-header-icon">
|
||||
<span class="icon">
|
||||
<i class="fas fa-newspaper"></i>
|
||||
</span>
|
||||
</span>
|
||||
<p class="card-header-title" title="message of the day">
|
||||
Onionr MOTD
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content motdContent">
|
||||
No MOTD currently.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="card transportCard">
|
||||
<header class="card-header">
|
||||
<span class="card-header-icon">
|
||||
<span class="icon">
|
||||
<img src="/shared/images/privacy.png" alt="">
|
||||
</span>
|
||||
</span>
|
||||
<p class="card-header-title">
|
||||
Networking Statistics
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content torInfo">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<span class="card-header-icon">
|
||||
<span class="icon">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
</span>
|
||||
</span>
|
||||
<p class="card-header-title">
|
||||
Dashboard
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
<a class="button is-static">
|
||||
<i class="fas fa-fingerprint"></i>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control is-expanded">
|
||||
<input id="myPub" class="input myPub" type="text" readonly>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a id="myPubCopy" class="button is-primary"><i class="fas fa-copy"></i></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<span class="icon">
|
||||
<i class="fas fa-lock"></i>
|
||||
</span>
|
||||
Security level: <span id="securityLevel"></span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<i class="fas fa-clock"></i>
|
||||
Uptime: <span id="uptime"></span>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
<button class="button is-info">
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-cubes"></i>
|
||||
</span>
|
||||
<span>Blocks</span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button class="button is-info">
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-book-open"></i>
|
||||
</span>
|
||||
<span>Help Book</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<h4>Connections</h4>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
️ Last Received: <span id="lastIncoming">None since start</span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<i class="fas fa-arrow-down"></i>
|
||||
Total Requests: <span id="totalRec">None since start</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field has-addons torTransportField">
|
||||
<p class="control">
|
||||
<a class="button is-static">
|
||||
<i class="fas fa-adjust"></i>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control is-expanded">
|
||||
<input class="input myTor" type="text" readonly>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-primary myTorCopy"><i class="fas fa-copy"></i></a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<i class="fas fa-link"></i>
|
||||
Outgoing Connections:
|
||||
<div class="container connectedNodesControl">
|
||||
<pre id="connectedNodes">Unable to get nodes</pre>
|
||||
</div>
|
||||
<br>
|
||||
<div class="field">
|
||||
<p class="control">
|
||||
<a class="button is-light restartTor" data-tooltip="Use if there have been no incoming or outgoing connections in a long time">Restart Tor</a>
|
||||
</p>
|
||||
</div>
|
||||
<br>
|
||||
<h4>Blocks</h4>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<i class="fas fa-hdd"></i>
|
||||
Stored blocks: <span id="storedBlocks"></span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<i class="fas fa-mail-bulk"></i>
|
||||
Blocks to download: <span id="blockQueue"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<p class="buttons">
|
||||
<button class="button is-small recentBlocksBtn">
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-stream"></i>
|
||||
</span>
|
||||
<span>Recent blocks</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<h4>Process</h4>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<i class="fas fa-microchip"></i>
|
||||
Current threads: <span id="threads"></span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<i class="fas fa-memory"></i>
|
||||
RAM usage: <span id="ramPercent"></span>
|
||||
</div>
|
||||
</div>
|
||||
<h4>Storage</h4>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
File descriptors: <span id="fileDescriptors"></span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<i class="fas fa-hdd"></i>
|
||||
Disk Usage: <span id="diskUsage"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="button is-white" id="configToggle">Configuration</a>
|
||||
|
||||
<div id="configContent">
|
||||
<div class="content">
|
||||
<p><em>Warning: </em><b>Some values can be dangerous to change. Use caution.</b></p>
|
||||
<textarea class="textarea configEditor" rows="20"></textarea>
|
||||
<br>
|
||||
<a class="button is-primary saveConfig">Save Config</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="particles-js"></div>
|
||||
<br>
|
||||
<div class="modal aboutModal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<button class="closeAboutModal delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body aboutBody">
|
||||
Loading... <i class="fas fa-spinner fa-spin"></i>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal recentModal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<button class="closeRecentModal delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body recentBody">
|
||||
Keep this open to see new blocks as they come in (or are created)!
|
||||
<pre class="recentBlockList"></pre>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,2 +0,0 @@
|
||||
console.log('%c Welcome to Onionr 🧅', 'border: 1px solid purple; float: left; font-weight: bold; font-size: 50px;color: purple; padding: 3em; background-color: black;');
|
||||
console.log('See the documentation at https://gitlab.com/beardog/Onionr/tree/master/docs')
|
@ -1,13 +0,0 @@
|
||||
fetch('/getmotd', {
|
||||
headers: {
|
||||
"token": webpass
|
||||
}})
|
||||
.then((resp) => resp.text())
|
||||
.then(function(resp) {
|
||||
resp = resp.trim()
|
||||
if (resp.length <= 1){return}
|
||||
let motds = document.getElementsByClassName("motdContent")
|
||||
for (x = 0; x < motds.length; x++){
|
||||
motds[x].innerText = resp
|
||||
}
|
||||
})
|
@ -1,63 +0,0 @@
|
||||
.idLink{
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently
|
||||
supported by Chrome and Opera */
|
||||
}
|
||||
#refreshStats{
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.motdCard{
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#connectedNodes{
|
||||
overflow-y: hidden;
|
||||
max-height: 300px;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
#configToggle, #configContent{
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.modal img{
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.torStats{
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* ---- reset ---- */
|
||||
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
/* ---- particles.js container ---- */
|
||||
|
||||
#particles-js {
|
||||
position:absolute;
|
||||
width:100%;
|
||||
height:100%;
|
||||
top:0;
|
||||
z-index:-1;
|
||||
}
|
||||
|
||||
.mainCont{
|
||||
z-index: 2;
|
||||
position: fixed;
|
||||
|
||||
}
|
||||
|
||||
.torTransportField {
|
||||
margin-top: 2em;
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
<img src="shared/images/onionr-text.png" class="aboutLogo" alt="Onionr">
|
||||
|
||||
<p>Onionr is a private decentralized communication network</p>
|
||||
<p><i class="fab fa-gitlab"></i> <a href="https://gitlab.com/beardog/onionr">Source code</a></p>
|
||||
<p><i class="fa fa-dollar-sign"></i> Please donate to keep the project alive. See info in readme</p>
|
||||
<br><br>
|
||||
|
||||
<b>Core developers:</b>
|
||||
<ul>
|
||||
<li><a href="https://www.chaoswebs.net/">Kevin Froman</a> - original author & project lead</li>
|
||||
</ul>
|
||||
|
||||
<b>Contributors:</b>
|
||||
<ul>
|
||||
<li><a href="https://www.aaronesau.com/">Aaron Esau</a> logger, config modules + advice/support</li>
|
||||
<li><a href="https://invisamage.com/">Travis Kipp</a> CSS help</li>
|
||||
<li><a href="https://k7dxs.net/">Duncan Simpson</a> packaging help</li>
|
||||
<li><a href="https://www.siue.edu/~njohnag/">Nicholas Johnson</a> bug fixes</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
|
||||
<b>Onionr is built with:</b>
|
||||
<br>
|
||||
<ul>
|
||||
<li><a href="https://torproject.org/">Tor</a> - Onionr routes all traffic through Tor by default - 3-clause BSD license</li>
|
||||
<li><a href="https://stem.torproject.org/">Stem</a> - Python library to interact with Tor - LGPLv3</li>
|
||||
<li><a href="https://palletsprojects.com/p/flask/">Flask</a> - Lightweight Python web framework - 3-clause BSD license</li>
|
||||
<li><a href="http://gevent.org">Gevent</a> - For the thread-safe WSGI servers - MIT license</li>
|
||||
<li><a href="https://2.python-requests.org/en/master/">Requests</a> - HTTP requests for humans - Apache 2.0 license</li>
|
||||
<li><a href="https://github.com/pyca/pynacl/">PyNaCl</a> - Python libsodium binding - Apache 2.0 license</li>
|
||||
<li><a href="https://download.libsodium.org/doc/">libsodium</a> - modern crypto library - ISC license</li>
|
||||
<li><a href="https://fontawesome.com/license/free">Font Awesome</a> - Icon set and toolkit - MIT license & CC-By 4.0</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<a href="https://bulma.io/"><img src="private/images/made-with-bulma--dark.png" class="aboutLogo" alt="made with Bulma"></a>
|
||||
<a href="https://python.org/"><img src="private/images/python-powered.png" class="aboutLogo" alt="made with Python"></a>
|
@ -1,469 +0,0 @@
|
||||
/*
|
||||
* [hi-base32]{@link https://github.com/emn178/hi-base32}
|
||||
*
|
||||
* @version 0.5.0
|
||||
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
||||
* @copyright Chen, Yi-Cyuan 2015-2018
|
||||
* @license MIT
|
||||
* /*
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
/*jslint bitwise: true */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var root = typeof window === 'object' ? window : {};
|
||||
var NODE_JS = !root.HI_BASE32_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
|
||||
if (NODE_JS) {
|
||||
root = global;
|
||||
}
|
||||
var COMMON_JS = !root.HI_BASE32_NO_COMMON_JS && typeof module === 'object' && module.exports;
|
||||
var AMD = typeof define === 'function' && define.amd;
|
||||
var BASE32_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.split('');
|
||||
var BASE32_DECODE_CHAR = {
|
||||
'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8,
|
||||
'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16,
|
||||
'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24,
|
||||
'Z': 25, '2': 26, '3': 27, '4': 28, '5': 29, '6': 30, '7': 31
|
||||
};
|
||||
|
||||
var blocks = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
var throwInvalidUtf8 = function (position, partial) {
|
||||
if (partial.length > 10) {
|
||||
partial = '...' + partial.substr(-10);
|
||||
}
|
||||
var err = new Error('Decoded data is not valid UTF-8.'
|
||||
+ ' Maybe try base32.decode.asBytes()?'
|
||||
+ ' Partial data after reading ' + position + ' bytes: ' + partial + ' <-');
|
||||
err.position = position;
|
||||
throw err;
|
||||
};
|
||||
|
||||
var toUtf8String = function (bytes) {
|
||||
var str = '', length = bytes.length, i = 0, followingChars = 0, b, c;
|
||||
while (i < length) {
|
||||
b = bytes[i++];
|
||||
if (b <= 0x7F) {
|
||||
str += String.fromCharCode(b);
|
||||
continue;
|
||||
} else if (b > 0xBF && b <= 0xDF) {
|
||||
c = b & 0x1F;
|
||||
followingChars = 1;
|
||||
} else if (b <= 0xEF) {
|
||||
c = b & 0x0F;
|
||||
followingChars = 2;
|
||||
} else if (b <= 0xF7) {
|
||||
c = b & 0x07;
|
||||
followingChars = 3;
|
||||
} else {
|
||||
throwInvalidUtf8(i, str);
|
||||
}
|
||||
|
||||
for (var j = 0; j < followingChars; ++j) {
|
||||
b = bytes[i++];
|
||||
if (b < 0x80 || b > 0xBF) {
|
||||
throwInvalidUtf8(i, str);
|
||||
}
|
||||
c <<= 6;
|
||||
c += b & 0x3F;
|
||||
}
|
||||
if (c >= 0xD800 && c <= 0xDFFF) {
|
||||
throwInvalidUtf8(i, str);
|
||||
}
|
||||
if (c > 0x10FFFF) {
|
||||
throwInvalidUtf8(i, str);
|
||||
}
|
||||
|
||||
if (c <= 0xFFFF) {
|
||||
str += String.fromCharCode(c);
|
||||
} else {
|
||||
c -= 0x10000;
|
||||
str += String.fromCharCode((c >> 10) + 0xD800);
|
||||
str += String.fromCharCode((c & 0x3FF) + 0xDC00);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
var decodeAsBytes = function (base32Str) {
|
||||
if (!/^[A-Z2-7=]+$/.test(base32Str)) {
|
||||
throw new Error('Invalid base32 characters');
|
||||
}
|
||||
base32Str = base32Str.replace(/=/g, '');
|
||||
var v1, v2, v3, v4, v5, v6, v7, v8, bytes = [], index = 0, length = base32Str.length;
|
||||
|
||||
// 4 char to 3 bytes
|
||||
for (var i = 0, count = length >> 3 << 3; i < count;) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
||||
bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
|
||||
bytes[index++] = (v4 << 4 | v5 >>> 1) & 255;
|
||||
bytes[index++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255;
|
||||
bytes[index++] = (v7 << 5 | v8) & 255;
|
||||
}
|
||||
|
||||
// remain bytes
|
||||
var remain = length - count;
|
||||
if (remain === 2) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
||||
} else if (remain === 4) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
||||
bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
|
||||
} else if (remain === 5) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
||||
bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
|
||||
bytes[index++] = (v4 << 4 | v5 >>> 1) & 255;
|
||||
} else if (remain === 7) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
||||
bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
|
||||
bytes[index++] = (v4 << 4 | v5 >>> 1) & 255;
|
||||
bytes[index++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255;
|
||||
}
|
||||
return bytes;
|
||||
};
|
||||
|
||||
var encodeAscii = function (str) {
|
||||
var v1, v2, v3, v4, v5, base32Str = '', length = str.length;
|
||||
for (var i = 0, count = parseInt(length / 5) * 5; i < count;) {
|
||||
v1 = str.charCodeAt(i++);
|
||||
v2 = str.charCodeAt(i++);
|
||||
v3 = str.charCodeAt(i++);
|
||||
v4 = str.charCodeAt(i++);
|
||||
v5 = str.charCodeAt(i++);
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] +
|
||||
BASE32_ENCODE_CHAR[v5 & 31];
|
||||
}
|
||||
|
||||
// remain char
|
||||
var remain = length - count;
|
||||
if (remain === 1) {
|
||||
v1 = str.charCodeAt(i);
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2) & 31] +
|
||||
'======';
|
||||
} else if (remain === 2) {
|
||||
v1 = str.charCodeAt(i++);
|
||||
v2 = str.charCodeAt(i);
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4) & 31] +
|
||||
'====';
|
||||
} else if (remain === 3) {
|
||||
v1 = str.charCodeAt(i++);
|
||||
v2 = str.charCodeAt(i++);
|
||||
v3 = str.charCodeAt(i);
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v3 << 1) & 31] +
|
||||
'===';
|
||||
} else if (remain === 4) {
|
||||
v1 = str.charCodeAt(i++);
|
||||
v2 = str.charCodeAt(i++);
|
||||
v3 = str.charCodeAt(i++);
|
||||
v4 = str.charCodeAt(i);
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 << 3) & 31] +
|
||||
'=';
|
||||
}
|
||||
return base32Str;
|
||||
};
|
||||
|
||||
var encodeUtf8 = function (str) {
|
||||
var v1, v2, v3, v4, v5, code, end = false, base32Str = '',
|
||||
index = 0, i, start = 0, bytes = 0, length = str.length;
|
||||
do {
|
||||
blocks[0] = blocks[5];
|
||||
blocks[1] = blocks[6];
|
||||
blocks[2] = blocks[7];
|
||||
for (i = start; index < length && i < 5; ++index) {
|
||||
code = str.charCodeAt(index);
|
||||
if (code < 0x80) {
|
||||
blocks[i++] = code;
|
||||
} else if (code < 0x800) {
|
||||
blocks[i++] = 0xc0 | (code >> 6);
|
||||
blocks[i++] = 0x80 | (code & 0x3f);
|
||||
} else if (code < 0xd800 || code >= 0xe000) {
|
||||
blocks[i++] = 0xe0 | (code >> 12);
|
||||
blocks[i++] = 0x80 | ((code >> 6) & 0x3f);
|
||||
blocks[i++] = 0x80 | (code & 0x3f);
|
||||
} else {
|
||||
code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(++index) & 0x3ff));
|
||||
blocks[i++] = 0xf0 | (code >> 18);
|
||||
blocks[i++] = 0x80 | ((code >> 12) & 0x3f);
|
||||
blocks[i++] = 0x80 | ((code >> 6) & 0x3f);
|
||||
blocks[i++] = 0x80 | (code & 0x3f);
|
||||
}
|
||||
}
|
||||
bytes += i - start;
|
||||
start = i - 5;
|
||||
if (index === length) {
|
||||
++index;
|
||||
}
|
||||
if (index > length && i < 6) {
|
||||
end = true;
|
||||
}
|
||||
v1 = blocks[0];
|
||||
if (i > 4) {
|
||||
v2 = blocks[1];
|
||||
v3 = blocks[2];
|
||||
v4 = blocks[3];
|
||||
v5 = blocks[4];
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] +
|
||||
BASE32_ENCODE_CHAR[v5 & 31];
|
||||
} else if (i === 1) {
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2) & 31] +
|
||||
'======';
|
||||
} else if (i === 2) {
|
||||
v2 = blocks[1];
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4) & 31] +
|
||||
'====';
|
||||
} else if (i === 3) {
|
||||
v2 = blocks[1];
|
||||
v3 = blocks[2];
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v3 << 1) & 31] +
|
||||
'===';
|
||||
} else {
|
||||
v2 = blocks[1];
|
||||
v3 = blocks[2];
|
||||
v4 = blocks[3];
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 << 3) & 31] +
|
||||
'=';
|
||||
}
|
||||
} while (!end);
|
||||
return base32Str;
|
||||
};
|
||||
|
||||
var encodeBytes = function (bytes) {
|
||||
var v1, v2, v3, v4, v5, base32Str = '', length = bytes.length;
|
||||
for (var i = 0, count = parseInt(length / 5) * 5; i < count;) {
|
||||
v1 = bytes[i++];
|
||||
v2 = bytes[i++];
|
||||
v3 = bytes[i++];
|
||||
v4 = bytes[i++];
|
||||
v5 = bytes[i++];
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] +
|
||||
BASE32_ENCODE_CHAR[v5 & 31];
|
||||
}
|
||||
|
||||
// remain char
|
||||
var remain = length - count;
|
||||
if (remain === 1) {
|
||||
v1 = bytes[i];
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2) & 31] +
|
||||
'======';
|
||||
} else if (remain === 2) {
|
||||
v1 = bytes[i++];
|
||||
v2 = bytes[i];
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4) & 31] +
|
||||
'====';
|
||||
} else if (remain === 3) {
|
||||
v1 = bytes[i++];
|
||||
v2 = bytes[i++];
|
||||
v3 = bytes[i];
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v3 << 1) & 31] +
|
||||
'===';
|
||||
} else if (remain === 4) {
|
||||
v1 = bytes[i++];
|
||||
v2 = bytes[i++];
|
||||
v3 = bytes[i++];
|
||||
v4 = bytes[i];
|
||||
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
||||
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
||||
BASE32_ENCODE_CHAR[(v4 << 3) & 31] +
|
||||
'=';
|
||||
}
|
||||
return base32Str;
|
||||
};
|
||||
|
||||
var encode = function (input, asciiOnly) {
|
||||
var notString = typeof(input) !== 'string';
|
||||
if (notString && input.constructor === ArrayBuffer) {
|
||||
input = new Uint8Array(input);
|
||||
}
|
||||
if (notString) {
|
||||
return encodeBytes(input);
|
||||
} else if (asciiOnly) {
|
||||
return encodeAscii(input);
|
||||
} else {
|
||||
return encodeUtf8(input);
|
||||
}
|
||||
};
|
||||
|
||||
var decode = function (base32Str, asciiOnly) {
|
||||
if (!asciiOnly) {
|
||||
return toUtf8String(decodeAsBytes(base32Str));
|
||||
}
|
||||
if (!/^[A-Z2-7=]+$/.test(base32Str)) {
|
||||
throw new Error('Invalid base32 characters');
|
||||
}
|
||||
var v1, v2, v3, v4, v5, v6, v7, v8, str = '', length = base32Str.indexOf('=');
|
||||
if (length === -1) {
|
||||
length = base32Str.length;
|
||||
}
|
||||
|
||||
// 8 char to 5 bytes
|
||||
for (var i = 0, count = length >> 3 << 3; i < count;) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) +
|
||||
String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) +
|
||||
String.fromCharCode((v4 << 4 | v5 >>> 1) & 255) +
|
||||
String.fromCharCode((v5 << 7 | v6 << 2 | v7 >>> 3) & 255) +
|
||||
String.fromCharCode((v7 << 5 | v8) & 255);
|
||||
}
|
||||
|
||||
// remain bytes
|
||||
var remain = length - count;
|
||||
if (remain === 2) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255);
|
||||
} else if (remain === 4) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) +
|
||||
String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255);
|
||||
} else if (remain === 5) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) +
|
||||
String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) +
|
||||
String.fromCharCode((v4 << 4 | v5 >>> 1) & 255);
|
||||
} else if (remain === 7) {
|
||||
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
||||
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) +
|
||||
String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) +
|
||||
String.fromCharCode((v4 << 4 | v5 >>> 1) & 255) +
|
||||
String.fromCharCode((v5 << 7 | v6 << 2 | v7 >>> 3) & 255);
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
var exports = {
|
||||
encode: encode,
|
||||
decode: decode
|
||||
};
|
||||
decode.asBytes = decodeAsBytes;
|
||||
|
||||
if (COMMON_JS) {
|
||||
module.exports = exports;
|
||||
} else {
|
||||
root.base32 = exports;
|
||||
if (AMD) {
|
||||
define(function() {
|
||||
return exports;
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
@ -1,436 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Wikiki
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory();
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define([], factory);
|
||||
else if(typeof exports === 'object')
|
||||
exports["bulmaQuickview"] = factory();
|
||||
else
|
||||
root["bulmaQuickview"] = factory();
|
||||
})(typeof self !== 'undefined' ? self : this, function() {
|
||||
return /******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__events__ = __webpack_require__(1);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__defaultOptions__ = __webpack_require__(2);
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
||||
|
||||
|
||||
|
||||
|
||||
var onQuickviewShowClick = Symbol('onQuickviewShowClick');
|
||||
var onQuickviewDismissClick = Symbol('onQuickviewDismissClick');
|
||||
|
||||
var bulmaQuickview = function (_EventEmitter) {
|
||||
_inherits(bulmaQuickview, _EventEmitter);
|
||||
|
||||
function bulmaQuickview(selector) {
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
|
||||
_classCallCheck(this, bulmaQuickview);
|
||||
|
||||
var _this = _possibleConstructorReturn(this, (bulmaQuickview.__proto__ || Object.getPrototypeOf(bulmaQuickview)).call(this));
|
||||
|
||||
_this.element = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
||||
// An invalid selector or non-DOM node has been provided.
|
||||
if (!_this.element) {
|
||||
throw new Error('An invalid selector or non-DOM node has been provided.');
|
||||
}
|
||||
|
||||
_this._clickEvents = ['click'];
|
||||
/// Set default options and merge with instance defined
|
||||
_this.options = _extends({}, __WEBPACK_IMPORTED_MODULE_1__defaultOptions__["a" /* default */], options);
|
||||
|
||||
_this[onQuickviewShowClick] = _this[onQuickviewShowClick].bind(_this);
|
||||
_this[onQuickviewDismissClick] = _this[onQuickviewDismissClick].bind(_this);
|
||||
|
||||
_this.init();
|
||||
return _this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate all DOM element containing carousel class
|
||||
* @method
|
||||
* @return {Array} Array of all Carousel instances
|
||||
*/
|
||||
|
||||
|
||||
_createClass(bulmaQuickview, [{
|
||||
key: 'init',
|
||||
|
||||
|
||||
/**
|
||||
* Initiate plugin
|
||||
* @method init
|
||||
* @return {void}
|
||||
*/
|
||||
value: function init() {
|
||||
this.quickview = document.getElementById(this.element.dataset['target']);
|
||||
this.dismissElements = document.querySelectorAll('[data-dismiss="quickview"]');
|
||||
|
||||
this._bindEvents();
|
||||
|
||||
this.emit('quickview:ready', {
|
||||
element: this.element,
|
||||
quickview: this.quickview
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all events
|
||||
* @method _bindEvents
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_bindEvents',
|
||||
value: function _bindEvents() {
|
||||
var _this2 = this;
|
||||
|
||||
this._clickEvents.forEach(function (event) {
|
||||
_this2.element.addEventListener(event, _this2[onQuickviewShowClick], false);
|
||||
});
|
||||
|
||||
[].forEach.call(this.dismissElements, function (dismissElement) {
|
||||
_this2._clickEvents.forEach(function (event) {
|
||||
dismissElement.addEventListener(event, _this2[onQuickviewDismissClick], false);
|
||||
});
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: onQuickviewShowClick,
|
||||
value: function value(e) {
|
||||
this.quickview.classList.add('is-active');
|
||||
|
||||
this.emit('quickview:show', {
|
||||
element: this.element,
|
||||
quickview: this.quickview
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: onQuickviewDismissClick,
|
||||
value: function value(e) {
|
||||
this.quickview.classList.remove('is-active');
|
||||
|
||||
this.emit('quickview:hide', {
|
||||
element: this.element,
|
||||
quickview: this.quickview
|
||||
});
|
||||
}
|
||||
}], [{
|
||||
key: 'attach',
|
||||
value: function attach() {
|
||||
var selector = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '[data-show="quickview"]';
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
|
||||
var instances = new Array();
|
||||
|
||||
var elements = document.querySelectorAll(selector);
|
||||
[].forEach.call(elements, function (element) {
|
||||
setTimeout(function () {
|
||||
instances.push(new bulmaQuickview(element, options));
|
||||
}, 100);
|
||||
});
|
||||
return instances;
|
||||
}
|
||||
}]);
|
||||
|
||||
return bulmaQuickview;
|
||||
}(__WEBPACK_IMPORTED_MODULE_0__events__["a" /* default */]);
|
||||
|
||||
/* harmony default export */ __webpack_exports__["default"] = (bulmaQuickview);
|
||||
|
||||
/***/ }),
|
||||
/* 1 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
var EventEmitter = function () {
|
||||
function EventEmitter() {
|
||||
var listeners = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
||||
|
||||
_classCallCheck(this, EventEmitter);
|
||||
|
||||
this._listeners = new Map(listeners);
|
||||
this._middlewares = new Map();
|
||||
}
|
||||
|
||||
_createClass(EventEmitter, [{
|
||||
key: "listenerCount",
|
||||
value: function listenerCount(eventName) {
|
||||
if (!this._listeners.has(eventName)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var eventListeners = this._listeners.get(eventName);
|
||||
return eventListeners.length;
|
||||
}
|
||||
}, {
|
||||
key: "removeListeners",
|
||||
value: function removeListeners() {
|
||||
var _this = this;
|
||||
|
||||
var eventName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
||||
var middleware = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
||||
|
||||
if (eventName !== null) {
|
||||
if (Array.isArray(eventName)) {
|
||||
name.forEach(function (e) {
|
||||
return _this.removeListeners(e, middleware);
|
||||
});
|
||||
} else {
|
||||
this._listeners.delete(eventName);
|
||||
|
||||
if (middleware) {
|
||||
this.removeMiddleware(eventName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._listeners = new Map();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "middleware",
|
||||
value: function middleware(eventName, fn) {
|
||||
var _this2 = this;
|
||||
|
||||
if (Array.isArray(eventName)) {
|
||||
name.forEach(function (e) {
|
||||
return _this2.middleware(e, fn);
|
||||
});
|
||||
} else {
|
||||
if (!Array.isArray(this._middlewares.get(eventName))) {
|
||||
this._middlewares.set(eventName, []);
|
||||
}
|
||||
|
||||
this._middlewares.get(eventName).push(fn);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "removeMiddleware",
|
||||
value: function removeMiddleware() {
|
||||
var _this3 = this;
|
||||
|
||||
var eventName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
||||
|
||||
if (eventName !== null) {
|
||||
if (Array.isArray(eventName)) {
|
||||
name.forEach(function (e) {
|
||||
return _this3.removeMiddleware(e);
|
||||
});
|
||||
} else {
|
||||
this._middlewares.delete(eventName);
|
||||
}
|
||||
} else {
|
||||
this._middlewares = new Map();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "on",
|
||||
value: function on(name, callback) {
|
||||
var _this4 = this;
|
||||
|
||||
var once = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
||||
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(function (e) {
|
||||
return _this4.on(e, callback);
|
||||
});
|
||||
} else {
|
||||
name = name.toString();
|
||||
var split = name.split(/,|, | /);
|
||||
|
||||
if (split.length > 1) {
|
||||
split.forEach(function (e) {
|
||||
return _this4.on(e, callback);
|
||||
});
|
||||
} else {
|
||||
if (!Array.isArray(this._listeners.get(name))) {
|
||||
this._listeners.set(name, []);
|
||||
}
|
||||
|
||||
this._listeners.get(name).push({ once: once, callback: callback });
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "once",
|
||||
value: function once(name, callback) {
|
||||
this.on(name, callback, true);
|
||||
}
|
||||
}, {
|
||||
key: "emit",
|
||||
value: function emit(name, data) {
|
||||
var _this5 = this;
|
||||
|
||||
var silent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
||||
|
||||
name = name.toString();
|
||||
var listeners = this._listeners.get(name);
|
||||
var middlewares = null;
|
||||
var doneCount = 0;
|
||||
var execute = silent;
|
||||
|
||||
if (Array.isArray(listeners)) {
|
||||
listeners.forEach(function (listener, index) {
|
||||
// Start Middleware checks unless we're doing a silent emit
|
||||
if (!silent) {
|
||||
middlewares = _this5._middlewares.get(name);
|
||||
// Check and execute Middleware
|
||||
if (Array.isArray(middlewares)) {
|
||||
middlewares.forEach(function (middleware) {
|
||||
middleware(data, function () {
|
||||
var newData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
||||
|
||||
if (newData !== null) {
|
||||
data = newData;
|
||||
}
|
||||
doneCount++;
|
||||
}, name);
|
||||
});
|
||||
|
||||
if (doneCount >= middlewares.length) {
|
||||
execute = true;
|
||||
}
|
||||
} else {
|
||||
execute = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If Middleware checks have been passed, execute
|
||||
if (execute) {
|
||||
if (listener.once) {
|
||||
listeners[index] = null;
|
||||
}
|
||||
listener.callback(data);
|
||||
}
|
||||
});
|
||||
|
||||
// Dirty way of removing used Events
|
||||
while (listeners.indexOf(null) !== -1) {
|
||||
listeners.splice(listeners.indexOf(null), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
return EventEmitter;
|
||||
}();
|
||||
|
||||
/* harmony default export */ __webpack_exports__["a"] = (EventEmitter);
|
||||
|
||||
/***/ }),
|
||||
/* 2 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
var defaultOptions = {};
|
||||
|
||||
/* harmony default export */ __webpack_exports__["a"] = (defaultOptions);
|
||||
|
||||
/***/ })
|
||||
/******/ ])["default"];
|
||||
});
|
24
static-data/www/shared/bulma-quickview.min.css
vendored
24
static-data/www/shared/bulma-quickview.min.css
vendored
@ -1,24 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Wikiki
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
@-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.quickview{display:flex;flex-direction:column;background-color:#fff;max-width:calc(100% - 50px);position:fixed;top:0;bottom:0;z-index:35;-webkit-transform:translateZ(0);transform:translateZ(0);transition:.3s ease;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;perspective:1000;will-change:transform}.quickview.is-marginless{max-width:100%}@media screen and (max-width:768px){.quickview{width:100%;right:-100%}}@media screen and (min-width:769px),print{.quickview{width:50%;right:-50%}}@media screen and (min-width:1088px){.quickview{width:35%;right:-35%}}@media screen and (min-width:1280px){.quickview{width:30%;right:-30%}}@media screen and (min-width:1472px){.quickview{width:25%;right:-25%}}@media screen and (max-width:768px){.quickview.is-left{left:-100%}}@media screen and (min-width:769px),print{.quickview.is-left{left:-50%}}@media screen and (min-width:1088px){.quickview.is-left{left:-35%}}@media screen and (min-width:1280px){.quickview.is-left{left:-30%}}@media screen and (min-width:1472px){.quickview.is-left{left:-25%}}.quickview.is-active{right:0;box-shadow:5px 0 13px 3px rgba(0,0,0,.1)}.quickview.is-active.is-left{left:0}.quickview-header{display:flex;justify-content:space-between;align-items:center;padding:0 1rem;min-height:3.25em!important;border-bottom:1px solid #dbdbdb}.quickview-header.is-white{background-color:#fff}.quickview-header.is-white .title{color:#0a0a0a}.quickview-header.is-black{background-color:#0a0a0a}.quickview-header.is-black .title{color:#fff}.quickview-header.is-light{background-color:#f5f5f5}.quickview-header.is-light .title{color:#363636}.quickview-header.is-dark{background-color:#363636}.quickview-header.is-dark .title{color:#f5f5f5}.quickview-header.is-primary{background-color:#00d1b2}.quickview-header.is-primary .title{color:#fff}.quickview-header.is-link{background-color:#3273dc}.quickview-header.is-link .title{color:#fff}.quickview-header.is-info{background-color:#209cee}.quickview-header.is-info .title{color:#fff}.quickview-header.is-success{background-color:#23d160}.quickview-header.is-success .title{color:#fff}.quickview-header.is-warning{background-color:#ffdd57}.quickview-header.is-warning .title{color:rgba(0,0,0,.7)}.quickview-header.is-danger{background-color:#ff3860}.quickview-header.is-danger .title{color:#fff}.quickview-header .title{font-size:1rem;font-weight:300;margin-bottom:0}.quickview-header .title img{max-height:2em}.quickview-body{flex:1 1 0%;overflow-y:auto}.quickview-footer{display:flex;justify-content:space-between;align-items:center;padding:0 1rem;min-height:4rem;background-color:#f5f5f5;border-top:1px solid #dbdbdb}.quickview-footer>*{margin:0 .4rem}
|
24
static-data/www/shared/bulma-steps.min.css
vendored
24
static-data/www/shared/bulma-steps.min.css
vendored
File diff suppressed because one or more lines are too long
618
static-data/www/shared/bulma-steps.min.js
vendored
618
static-data/www/shared/bulma-steps.min.js
vendored
@ -1,618 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Wikiki
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory();
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define([], factory);
|
||||
else if(typeof exports === 'object')
|
||||
exports["bulmaSteps"] = factory();
|
||||
else
|
||||
root["bulmaSteps"] = factory();
|
||||
})(typeof self !== 'undefined' ? self : this, function() {
|
||||
return /******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__events__ = __webpack_require__(1);
|
||||
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__defaultOptions__ = __webpack_require__(2);
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
||||
|
||||
|
||||
|
||||
|
||||
var onStepsPrevious = Symbol('onStepsPrevious');
|
||||
var onStepsNext = Symbol('onStepsNext');
|
||||
|
||||
var bulmaSteps = function (_EventEmitter) {
|
||||
_inherits(bulmaSteps, _EventEmitter);
|
||||
|
||||
function bulmaSteps(selector) {
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
|
||||
_classCallCheck(this, bulmaSteps);
|
||||
|
||||
var _this = _possibleConstructorReturn(this, (bulmaSteps.__proto__ || Object.getPrototypeOf(bulmaSteps)).call(this));
|
||||
|
||||
_this.element = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
||||
// An invalid selector or non-DOM node has been provided.
|
||||
if (!_this.element) {
|
||||
throw new Error('An invalid selector or non-DOM node has been provided.');
|
||||
}
|
||||
|
||||
_this._clickEvents = ['click'];
|
||||
/// Set default options and merge with instance defined
|
||||
_this.options = _extends({}, __WEBPACK_IMPORTED_MODULE_1__defaultOptions__["a" /* default */], options);
|
||||
|
||||
_this[onStepsPrevious] = _this[onStepsPrevious].bind(_this);
|
||||
_this[onStepsNext] = _this[onStepsNext].bind(_this);
|
||||
|
||||
_this.init();
|
||||
return _this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate all DOM element containing carousel class
|
||||
* @method
|
||||
* @return {Array} Array of all Carousel instances
|
||||
*/
|
||||
|
||||
|
||||
_createClass(bulmaSteps, [{
|
||||
key: 'init',
|
||||
|
||||
|
||||
/**
|
||||
* Initiate plugin
|
||||
* @method init
|
||||
* @return {void}
|
||||
*/
|
||||
value: function init() {
|
||||
this._id = 'bulmaSteps' + new Date().getTime() + Math.floor(Math.random() * Math.floor(9999));
|
||||
|
||||
this.steps = this.element.querySelectorAll(this.options.selector);
|
||||
this.contents = this.element.querySelectorAll(this.options.selector_content);
|
||||
this.previous_btn = this.element.querySelector(this.options.previous_selector);
|
||||
this.next_btn = this.element.querySelector(this.options.next_selector);
|
||||
|
||||
[].forEach.call(this.steps, function (step, index) {
|
||||
step.setAttribute('data-step-id', index);
|
||||
});
|
||||
|
||||
if (this.steps && this.steps.length) {
|
||||
this.activate_step(0);
|
||||
this.updateActions(this.steps[0]);
|
||||
}
|
||||
|
||||
this._bindEvents();
|
||||
|
||||
this.emit('bulmasteps:ready', this.element.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all events
|
||||
* @method _bindEvents
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_bindEvents',
|
||||
value: function _bindEvents() {
|
||||
var _this2 = this;
|
||||
|
||||
if (this.previous_btn != null) {
|
||||
this._clickEvents.forEach(function (event) {
|
||||
_this2.previous_btn.addEventListener(event, _this2[onStepsPrevious], false);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.next_btn != null) {
|
||||
this._clickEvents.forEach(function (event) {
|
||||
_this2.next_btn.addEventListener(event, _this2[onStepsNext], false);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options.stepClickable) {
|
||||
[].forEach.call(this.steps, function (step, index) {
|
||||
_this2._clickEvents.forEach(function (event) {
|
||||
while (index > _this2.current_id) {
|
||||
_this2[onStepsNext](event);
|
||||
}
|
||||
while (index < _this2.current_id) {
|
||||
_this2[onStepsPrevious](event);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: onStepsPrevious,
|
||||
value: function value(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!e.target.getAttribute('disabled')) {
|
||||
this.previous_step();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: onStepsNext,
|
||||
value: function value(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!e.target.getAttribute('disabled')) {
|
||||
this.next_step();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'get_current_step_id',
|
||||
value: function get_current_step_id() {
|
||||
for (var i = 0; i < this.steps.length; i++) {
|
||||
var step = this.steps[i];
|
||||
|
||||
if (step.classList.contains(this.options.active_class)) {
|
||||
return parseInt(step.getAttribute('data-step-id'));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}, {
|
||||
key: 'updateActions',
|
||||
value: function updateActions(step) {
|
||||
var stepId = parseInt(step.getAttribute('data-step-id'));
|
||||
if (stepId == 0) {
|
||||
if (this.previous_btn != null) {
|
||||
this.previous_btn.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
if (this.next_btn != null) {
|
||||
this.next_btn.removeAttribute('disabled', 'disabled');
|
||||
}
|
||||
} else if (stepId == this.steps.length - 1) {
|
||||
if (this.previous_btn != null) {
|
||||
this.previous_btn.removeAttribute('disabled', 'disabled');
|
||||
}
|
||||
if (this.next_btn != null) {
|
||||
this.next_btn.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
} else {
|
||||
if (this.previous_btn != null) {
|
||||
this.previous_btn.removeAttribute('disabled', 'disabled');
|
||||
}
|
||||
if (this.next_btn != null) {
|
||||
this.next_btn.removeAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'next_step',
|
||||
value: function next_step() {
|
||||
var current_id = this.get_current_step_id();
|
||||
|
||||
if (current_id == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var next_id = current_id + 1,
|
||||
errors = [];
|
||||
|
||||
if (typeof this.options.beforeNext != 'undefined' && this.options.beforeNext != null && this.options.beforeNext) {
|
||||
errors = this.options.beforeNext(current_id);
|
||||
}
|
||||
this.emit('bulmasteps:before:next', current_id);
|
||||
|
||||
if (typeof errors == 'undefined') {
|
||||
errors = [];
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
this.emit('bulmasteps:errors', errors);
|
||||
for (var i = 0; i < errors.length; i++) {
|
||||
if (typeof this.options.onError != 'undefined' && this.options.onError != null && this.options.onError) {
|
||||
this.options.onError(errors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_id >= this.steps.length) {
|
||||
if (typeof this.options.onFinish != 'undefined' && this.options.onFinish != null && this.options.onFinish) {
|
||||
this.options.onFinish(current_id);
|
||||
}
|
||||
this.emit('bulmasteps:finish', current_id);
|
||||
this.deactivate_step(current_id);
|
||||
} else {
|
||||
this.complete_step(current_id);
|
||||
this.activate_step(next_id);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'previous_step',
|
||||
value: function previous_step() {
|
||||
var current_id = this.get_current_step_id();
|
||||
if (current_id == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.uncomplete_step(current_id - 1);
|
||||
this.activate_step(current_id - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a single step,
|
||||
* will deactivate all other steps.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'activate_step',
|
||||
value: function activate_step(step_id) {
|
||||
this.updateActions(this.steps[step_id]);
|
||||
|
||||
for (var i = 0; i < this.steps.length; i++) {
|
||||
var _step = this.steps[i];
|
||||
|
||||
if (_step == this.steps[step_id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.deactivate_step(i);
|
||||
}
|
||||
|
||||
this.steps[step_id].classList.add(this.options.active_class);
|
||||
if (typeof this.contents[step_id] !== 'undefined') {
|
||||
this.contents[step_id].classList.add(this.options.active_class);
|
||||
}
|
||||
|
||||
if (typeof this.options.onShow != 'undefined' && this.options.onShow != null && this.options.onShow) {
|
||||
this.options.onShow(step_id);
|
||||
}
|
||||
|
||||
this.emit('bulmasteps:step:show', step_id);
|
||||
}
|
||||
}, {
|
||||
key: 'complete_step',
|
||||
value: function complete_step(step_id) {
|
||||
this.steps[step_id].classList.add(this.options.completed_class);
|
||||
this.emit('bulmasteps:step:completed', step_id);
|
||||
}
|
||||
}, {
|
||||
key: 'uncomplete_step',
|
||||
value: function uncomplete_step(step_id) {
|
||||
this.steps[step_id].classList.remove(this.options.completed_class);
|
||||
this.emit('bulmasteps:step:uncompleted', step_id);
|
||||
}
|
||||
}, {
|
||||
key: 'deactivate_step',
|
||||
value: function deactivate_step(step_id) {
|
||||
this.steps[step_id].classList.remove(this.options.active_class);
|
||||
if (typeof this.contents[step_id] !== 'undefined') {
|
||||
this.contents[step_id].classList.remove(this.options.active_class);
|
||||
}
|
||||
}
|
||||
}], [{
|
||||
key: 'attach',
|
||||
value: function attach() {
|
||||
var selector = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '.steps';
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
|
||||
var instances = new Array();
|
||||
|
||||
var elements = document.querySelectorAll(selector);
|
||||
[].forEach.call(elements, function (element) {
|
||||
setTimeout(function () {
|
||||
instances.push(new bulmaSteps(element, options));
|
||||
}, 100);
|
||||
});
|
||||
return instances;
|
||||
}
|
||||
}]);
|
||||
|
||||
return bulmaSteps;
|
||||
}(__WEBPACK_IMPORTED_MODULE_0__events__["a" /* default */]);
|
||||
|
||||
/* harmony default export */ __webpack_exports__["default"] = (bulmaSteps);
|
||||
|
||||
/***/ }),
|
||||
/* 1 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
var EventEmitter = function () {
|
||||
function EventEmitter() {
|
||||
var listeners = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
||||
|
||||
_classCallCheck(this, EventEmitter);
|
||||
|
||||
this._listeners = new Map(listeners);
|
||||
this._middlewares = new Map();
|
||||
}
|
||||
|
||||
_createClass(EventEmitter, [{
|
||||
key: "listenerCount",
|
||||
value: function listenerCount(eventName) {
|
||||
if (!this._listeners.has(eventName)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var eventListeners = this._listeners.get(eventName);
|
||||
return eventListeners.length;
|
||||
}
|
||||
}, {
|
||||
key: "removeListeners",
|
||||
value: function removeListeners() {
|
||||
var _this = this;
|
||||
|
||||
var eventName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
||||
var middleware = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
||||
|
||||
if (eventName !== null) {
|
||||
if (Array.isArray(eventName)) {
|
||||
name.forEach(function (e) {
|
||||
return _this.removeListeners(e, middleware);
|
||||
});
|
||||
} else {
|
||||
this._listeners.delete(eventName);
|
||||
|
||||
if (middleware) {
|
||||
this.removeMiddleware(eventName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._listeners = new Map();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "middleware",
|
||||
value: function middleware(eventName, fn) {
|
||||
var _this2 = this;
|
||||
|
||||
if (Array.isArray(eventName)) {
|
||||
name.forEach(function (e) {
|
||||
return _this2.middleware(e, fn);
|
||||
});
|
||||
} else {
|
||||
if (!Array.isArray(this._middlewares.get(eventName))) {
|
||||
this._middlewares.set(eventName, []);
|
||||
}
|
||||
|
||||
this._middlewares.get(eventName).push(fn);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "removeMiddleware",
|
||||
value: function removeMiddleware() {
|
||||
var _this3 = this;
|
||||
|
||||
var eventName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
||||
|
||||
if (eventName !== null) {
|
||||
if (Array.isArray(eventName)) {
|
||||
name.forEach(function (e) {
|
||||
return _this3.removeMiddleware(e);
|
||||
});
|
||||
} else {
|
||||
this._middlewares.delete(eventName);
|
||||
}
|
||||
} else {
|
||||
this._middlewares = new Map();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "on",
|
||||
value: function on(name, callback) {
|
||||
var _this4 = this;
|
||||
|
||||
var once = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
||||
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(function (e) {
|
||||
return _this4.on(e, callback);
|
||||
});
|
||||
} else {
|
||||
name = name.toString();
|
||||
var split = name.split(/,|, | /);
|
||||
|
||||
if (split.length > 1) {
|
||||
split.forEach(function (e) {
|
||||
return _this4.on(e, callback);
|
||||
});
|
||||
} else {
|
||||
if (!Array.isArray(this._listeners.get(name))) {
|
||||
this._listeners.set(name, []);
|
||||
}
|
||||
|
||||
this._listeners.get(name).push({ once: once, callback: callback });
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "once",
|
||||
value: function once(name, callback) {
|
||||
this.on(name, callback, true);
|
||||
}
|
||||
}, {
|
||||
key: "emit",
|
||||
value: function emit(name, data) {
|
||||
var _this5 = this;
|
||||
|
||||
var silent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
||||
|
||||
name = name.toString();
|
||||
var listeners = this._listeners.get(name);
|
||||
var middlewares = null;
|
||||
var doneCount = 0;
|
||||
var execute = silent;
|
||||
|
||||
if (Array.isArray(listeners)) {
|
||||
listeners.forEach(function (listener, index) {
|
||||
// Start Middleware checks unless we're doing a silent emit
|
||||
if (!silent) {
|
||||
middlewares = _this5._middlewares.get(name);
|
||||
// Check and execute Middleware
|
||||
if (Array.isArray(middlewares)) {
|
||||
middlewares.forEach(function (middleware) {
|
||||
middleware(data, function () {
|
||||
var newData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
||||
|
||||
if (newData !== null) {
|
||||
data = newData;
|
||||
}
|
||||
doneCount++;
|
||||
}, name);
|
||||
});
|
||||
|
||||
if (doneCount >= middlewares.length) {
|
||||
execute = true;
|
||||
}
|
||||
} else {
|
||||
execute = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If Middleware checks have been passed, execute
|
||||
if (execute) {
|
||||
if (listener.once) {
|
||||
listeners[index] = null;
|
||||
}
|
||||
listener.callback(data);
|
||||
}
|
||||
});
|
||||
|
||||
// Dirty way of removing used Events
|
||||
while (listeners.indexOf(null) !== -1) {
|
||||
listeners.splice(listeners.indexOf(null), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
return EventEmitter;
|
||||
}();
|
||||
|
||||
/* harmony default export */ __webpack_exports__["a"] = (EventEmitter);
|
||||
|
||||
/***/ }),
|
||||
/* 2 */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
var defaultOptions = {
|
||||
'selector': '.step-item',
|
||||
'selector_content': '.step-content',
|
||||
'previous_selector': '[data-nav="previous"]',
|
||||
'next_selector': '[data-nav="next"]',
|
||||
'active_class': 'is-active',
|
||||
'completed_class': 'is-completed',
|
||||
'stepClickable': false,
|
||||
'beforeNext': null,
|
||||
'onShow': null,
|
||||
'onFinish': null,
|
||||
'onError': null
|
||||
};
|
||||
|
||||
/* harmony default export */ __webpack_exports__["a"] = (defaultOptions);
|
||||
|
||||
/***/ })
|
||||
/******/ ])["default"];
|
||||
});
|
||||
bulmaSteps.attach();
|
24
static-data/www/shared/bulma-tooltip.min.css
vendored
24
static-data/www/shared/bulma-tooltip.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,56 +0,0 @@
|
||||
/*
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
This file is for configuration editing in the web interface
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
var saveBtns = document.getElementsByClassName('saveConfig')
|
||||
var saveBtn = document.getElementsByClassName('saveConfig')[0]
|
||||
var configEditor = document.getElementsByClassName('configEditor')[0]
|
||||
var config = {}
|
||||
|
||||
fetch('/config/get', {
|
||||
headers: {
|
||||
"token": webpass
|
||||
}})
|
||||
.then((resp) => resp.text()) // Transform the data into text
|
||||
.then(function(resp) {
|
||||
configEditor.value = resp
|
||||
config = JSON.parse(resp) //parse here so we can set the text field to pretty json
|
||||
})
|
||||
|
||||
saveBtn.onclick = function(){
|
||||
var postData = configEditor.value
|
||||
try {
|
||||
JSON.parse(postData)
|
||||
} catch (e) {
|
||||
alert('Configuration is not valid JSON')
|
||||
return false
|
||||
}
|
||||
fetch('/config/setall', {
|
||||
method: 'POST',
|
||||
body: postData,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"token": webpass
|
||||
}})
|
||||
.then((resp) => resp.text()) // Transform the data into text
|
||||
.then(function(data) {
|
||||
PNotify.success({
|
||||
text: 'Config saved'
|
||||
})
|
||||
})
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
Onionr - Private P2P Communication
|
||||
|
||||
Functions to detect
|
||||
|
||||
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/>
|
||||
*/
|
||||
direct_connections = {}
|
||||
|
||||
let waitForConnection = function(pubkey){
|
||||
fetch('/dc-client/isconnected/' + pubkey, {
|
||||
headers: {
|
||||
"token": webpass
|
||||
}})
|
||||
.then((resp) => resp.text())
|
||||
.then(function(resp) {
|
||||
if (resp === ""){
|
||||
// Try to get the client address again again in a few seconds
|
||||
setTimeout(function(){waitForConnection(pubkey)}, 3000)
|
||||
}
|
||||
else{
|
||||
// add to the dc object
|
||||
direct_connections[pubkey] = resp
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let createConnection = function(pubkey){
|
||||
// Tells the Onionr daemon to create a client connection to a remote peer for generic direct connections
|
||||
|
||||
// If the pubkey is already connected, don't bother
|
||||
if (direct_connections.hasOwnProperty(pubkey)){
|
||||
return
|
||||
}
|
||||
|
||||
// Do the request, then spawn a function to wait for the connection to be created
|
||||
fetch('/dc-client/connect/' + pubkey, {
|
||||
headers: {
|
||||
"token": webpass
|
||||
}})
|
||||
.then((resp) => resp.text())
|
||||
.then(function(resp) {
|
||||
if (resp === "pending"){
|
||||
setTimeout(function(){waitForConnection(pubkey)}, 3000)
|
||||
}
|
||||
else{
|
||||
direct_connections[pubkey] = resp
|
||||
}
|
||||
})
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,34 +0,0 @@
|
||||
Font Awesome Free License
|
||||
-------------------------
|
||||
|
||||
Font Awesome Free is free, open source, and GPL friendly. You can use it for
|
||||
commercial projects, open source projects, or really almost whatever you want.
|
||||
Full Font Awesome Free license: https://fontawesome.com/license/free.
|
||||
|
||||
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
|
||||
In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
|
||||
packaged as SVG and JS file types.
|
||||
|
||||
# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
|
||||
In the Font Awesome Free download, the SIL OFL license applies to all icons
|
||||
packaged as web and desktop font files.
|
||||
|
||||
# Code: MIT License (https://opensource.org/licenses/MIT)
|
||||
In the Font Awesome Free download, the MIT license applies to all non-font and
|
||||
non-icon files.
|
||||
|
||||
# Attribution
|
||||
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
|
||||
Awesome Free files already contain embedded comments with sufficient
|
||||
attribution, so you shouldn't need to do anything additional when using these
|
||||
files normally.
|
||||
|
||||
We've kept attribution comments terse, so we ask that you do not actively work
|
||||
to remove them from files, especially code. They're a great way for folks to
|
||||
learn about Font Awesome.
|
||||
|
||||
# Brand Icons
|
||||
All brand icons are trademarks of their respective owners. The use of these
|
||||
trademarks does not indicate endorsement of the trademark holder by Font
|
||||
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
||||
to represent the company, product, or service to which they refer.**
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user