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:
Kevin F 2022-08-03 00:23:22 -05:00
parent 228d3cbe35
commit c880b6fa7a
178 changed files with 76 additions and 20117 deletions

View File

@ -1,17 +1,10 @@
urllib3==1.26.7
requests==2.28.1
PyNaCl==1.5.0 PyNaCl==1.5.0
gevent==21.12.0
Flask==2.1.3
PySocks==1.7.1 PySocks==1.7.1
stem==1.8.0 stem==1.8.0
deadsimplekv==0.3.2
unpaddedbase32==0.2.0 unpaddedbase32==0.2.0
toomanyobjs==1.1.0
niceware==0.2.1 niceware==0.2.1
psutil==5.9.1 psutil==5.9.1
filenuke==0.0.0 filenuke==0.0.0
watchdog==2.1.9
ujson==5.4.0 ujson==5.4.0
cffi==1.15.1 cffi==1.15.1
onionrblocks==7.0.0 onionrblocks==7.0.0

View File

@ -4,36 +4,14 @@
# #
# pip-compile # pip-compile
# #
certifi==2018.11.29
# via requests
cffi==1.15.1 cffi==1.15.1
# via # via
# -r requirements.in # -r requirements.in
# pynacl # 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 filenuke==0.0.0
# via -r requirements.in # 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 kasten==3.0.0
# via onionrblocks # via onionrblocks
markupsafe==2.0.1
# via jinja2
mimcvdf==1.2.1 mimcvdf==1.2.1
# via kasten # via kasten
msgpack==1.0.3 msgpack==1.0.3
@ -54,28 +32,9 @@ pynacl==1.5.0
# onionrblocks # onionrblocks
pysocks==1.7.1 pysocks==1.7.1
# via -r requirements.in # via -r requirements.in
requests==2.28.1
# via -r requirements.in
stem==1.8.0 stem==1.8.0
# via -r requirements.in # via -r requirements.in
toomanyobjs==1.1.0
# via -r requirements.in
ujson==5.4.0 ujson==5.4.0
# via -r requirements.in # via -r requirements.in
unpaddedbase32==0.2.0 unpaddedbase32==0.2.0
# via -r requirements.in # 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

View File

@ -46,14 +46,6 @@ locale.setlocale(locale.LC_ALL, '') # noqa
ran_as_script = False ran_as_script = False
if __name__ == "__main__": ran_as_script = True 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 # Import 3rd party libraries
from filenuke import nuke # noqa from filenuke import nuke # noqa

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -20,6 +20,7 @@ upload_list = home + 'upload-list.json'
config_file = home + 'config.json' config_file = home + 'config.json'
daemon_mark_file = home + '/daemon-true.txt' daemon_mark_file = home + '/daemon-true.txt'
lock_file = home + 'onionr.lock' lock_file = home + 'onionr.lock'
pid_file = home + 'onionr.pid'
site_cache = home + 'onionr-sites.txt' site_cache = home + 'onionr-sites.txt'

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -1 +0,0 @@
from . import shutdown, setbindip

View File

@ -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

View File

@ -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")

View File

@ -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')

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -1 +0,0 @@
from . import staticfiles, endpoints

View File

@ -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())

View File

@ -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)

View File

@ -1 +0,0 @@
from . import client

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,6 +0,0 @@
# sse
This folder contains a wrapper for handling server sent event loops

View File

@ -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/>.
"""

View File

@ -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)

View File

@ -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

View File

@ -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')

View File

@ -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)

View File

@ -3,26 +3,20 @@
launch the api servers and communicator launch the api servers and communicator
""" """
import os import os
import queue from time import sleep
import sys import sys
import platform import platform
import signal import signal
from threading import Thread from threading import Thread
from stem.connection import IncorrectPassword
import toomanyobjs
import filenuke import filenuke
from deadsimplekv import DeadSimpleKV
import psutil import psutil
from ordered_set import OrderedSet
import config import config
import apiservers
import logger import logger
from onionrplugins import onionrevents as events from onionrplugins import onionrevents as events
from onionrutils import localcommand
from utils import identifyhome from utils import identifyhome
import filepaths import filepaths
import onionrvalues import onionrvalues
@ -30,13 +24,11 @@ from onionrutils import cleanup
from onionrcrypto import getourkeypair from onionrcrypto import getourkeypair
from onionrthreads import add_onionr_thread from onionrthreads import add_onionr_thread
from blockdb.blockcleaner import clean_block_database from blockdb.blockcleaner import clean_block_database
import runtests
from .. import version from .. import version
from .killdaemon import kill_daemon # noqa from .killdaemon import kill_daemon # noqa
from .showlogo import show_logo from .showlogo import show_logo
import gossip import gossip
from setupkvvars import setup_kv
""" """
This program is free software: you can redistribute it and/or modify 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 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(): def _show_info_messages():
version.version(verbosity=5, function=logger.info) version.version(verbosity=5, function=logger.info)
logger.debug('Python version %s' % platform.python_version()) 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.""" """Start Onionr's primary threads for communicator, API server, node, and LAN."""
def _handle_sig_term(signum, frame): def _handle_sig_term(signum, frame):
pid = str(os.getpid()) sys.exit(0)
main_pid = localcommand.local_command('/getpid')
#logger.info(main_pid, terminal=True) with open(filepaths.pid_file, 'w') as f:
if main_pid and main_pid == pid: f.write(str(os.getpid()))
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)
signal.signal(signal.SIGTERM, _handle_sig_term) 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() show_logo()
@ -121,14 +86,13 @@ def daemon():
target=gossip.start_gossip_threads, target=gossip.start_gossip_threads,
daemon=True, daemon=True,
name='start_gossip_threads').start() name='start_gossip_threads').start()
try: try:
apiservers.private_api.start() while True:
events.event('shutdown', threaded=False) sleep(60)
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
cleanup.delete_run_files() cleanup.delete_run_files()
if security_level >= 2: if security_level >= 2:
filenuke.nuke.clean_tree(identifyhome.identify_home()) filenuke.nuke.clean_tree(identifyhome.identify_home())

View File

@ -2,15 +2,11 @@
Gracefully stop Onionr daemon Gracefully stop Onionr daemon
""" """
import sqlite3
import os import os
from signal import SIGTERM
from gevent import spawn from filepaths import pid_file
from onionrplugins import events
from onionrutils import localcommand
import logger import logger
import config
""" """
This program is free software: you can redistribute it and/or modify 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 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(): def kill_daemon():
"""Shutdown the Onionr daemon (communicator).""" """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, logger.warn('Stopping the running daemon, if one exists...', timestamp=False,
terminal=True) 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 = "Gracefully stops the " # type: ignore
kill_daemon.onionr_help += "Onionr API servers" # type: ignore kill_daemon.onionr_help += "Onionr API servers" # type: ignore

View File

@ -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

View File

@ -5,12 +5,8 @@ Sets CLI arguments for Onionr
from typing import Callable from typing import Callable
from .. import onionrstatistics, version, daemonlaunch from .. import onionrstatistics, version, daemonlaunch
from .. import openwebinterface
from .. import pubkeymanager # commands to add or change id from .. import pubkeymanager # commands to add or change id
from .. import resetplugins # command to reinstall default plugins 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 import onionrexceptions
@ -42,15 +38,9 @@ def get_arguments() -> dict:
('version',): version.version, ('version',): version.version,
('start', 'daemon'): daemonlaunch.start, ('start', 'daemon'): daemonlaunch.start,
('stop', 'kill'): daemonlaunch.kill_daemon, ('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, ('addid', 'add-id'): pubkeymanager.add_ID,
('changeid', 'change-id'): pubkeymanager.change_ID, ('changeid', 'change-id'): pubkeymanager.change_ID,
('resetplugins', 'reset-plugins'): resetplugins.reset, ('resetplugins', 'reset-plugins'): resetplugins.reset
('soft-reset', 'softreset'): softreset.soft_reset,
('runtime-test', 'runtimetest'): runtimetestcmd.do_runtime_test
} }
return args return args

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,6 +1,5 @@
from . import safecompare, replayvalidation from . import safecompare
from . import getpubfrompriv from . import getpubfrompriv
replay_validator = replayvalidation.replay_timestamp_validation
safe_compare = safecompare.safe_compare safe_compare = safecompare.safe_compare
get_pub_key_from_priv = getpubfrompriv.get_pub_key_from_priv get_pub_key_from_priv = getpubfrompriv.get_pub_key_from_priv

View File

@ -1,3 +0,0 @@
from onionrutils import epoch
def replay_timestamp_validation(timestamp):
return epoch.get_epoch() - int(timestamp) <= 2419200

View File

@ -1,4 +1,6 @@
import hmac import hmac
def safe_compare(one, two): def safe_compare(one, two):
# Do encode here to avoid spawning core # Do encode here to avoid spawning core
try: try:
@ -9,4 +11,5 @@ def safe_compare(one, two):
two = two.encode() two = two.encode()
except AttributeError: except AttributeError:
pass pass
return hmac.compare_digest(one, two) return hmac.compare_digest(one, two)

View File

@ -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

View File

@ -19,7 +19,7 @@
''' '''
import onionrplugins, logger import onionrplugins, logger
from onionrutils import localcommand
class PluginAPI: class PluginAPI:
def __init__(self, pluginapi): def __init__(self, pluginapi):

View File

@ -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()

View File

@ -10,7 +10,6 @@ import ujson as json
import config import config
import logger import logger
import onionrvalues import onionrvalues
from onionrutils import getopenport
from logger.settings import * from logger.settings import *
from utils import readstatic from utils import readstatic
""" """
@ -77,14 +76,3 @@ def setup_config():
set_level(map[verbosity]) set_level(map[verbosity])
else: else:
logger.warn('Verbosity level %s is not valid, using default verbosity.' % verbosity) 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)

View File

@ -38,3 +38,4 @@ def delete_run_files():
_safe_remove(filepaths.daemon_mark_file) _safe_remove(filepaths.daemon_mark_file)
_safe_remove(filepaths.lock_file) _safe_remove(filepaths.lock_file)
_safe_remove(filepaths.gossip_server_socket_file) _safe_remove(filepaths.gossip_server_socket_file)
_safe_remove(filepaths.pid_file)

View File

@ -1 +0,0 @@
from urllib3.contrib.socks import SOCKSProxyManager # noqa

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,49 +1,30 @@
''' """
Onionr - Private P2P Communication Onionr - Private P2P Communication
validate various string data types validate various string data types
''' """
''' import base64
This program is free software: you can redistribute it and/or modify import string
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
import unpaddedbase32, nacl.signing, nacl.encoding import unpaddedbase32, nacl.signing, nacl.encoding
from onionrutils import bytesconverter from onionrutils import bytesconverter
def validate_hash(data, length=64): """
''' This program is free software: you can redistribute it and/or modify
Validate if a string is a valid hash hex digest (does not compare, just checks length and charset) 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 This program is distributed in the hope that it will be useful,
''' but WITHOUT ANY WARRANTY; without even the implied warranty of
retVal = True MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
if data == False or data == True: GNU General Public License for more details.
return False
data = data.strip() You should have received a copy of the GNU General Public License
if len(data) > length: along with this program. If not, see <https://www.gnu.org/licenses/>.
retVal = False """
else:
try:
int(data, 16)
except ValueError:
retVal = False
return retVal
def validate_pub_key(key): 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): if type(key) is type(None):
return False return False
# Accept keys that have no = padding # Accept keys that have no = padding
@ -54,18 +35,8 @@ def validate_pub_key(key):
nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder) nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
except nacl.exceptions.ValueError: except nacl.exceptions.ValueError:
pass pass
except base64.binascii.Error as err: except base64.binascii.Error as _:
pass pass
else: else:
retVal = True retVal = True
return retVal 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

View File

@ -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.")

View File

@ -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

View File

@ -23,54 +23,24 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
DENIABLE_PEER_ADDRESS = "OVPCZLOXD6DC5JHX4EQ3PSOGAZ3T24F75HQLIUZSDSMYPEOXCPFA" DENIABLE_PEER_ADDRESS = "OVPCZLOXD6DC5JHX4EQ3PSOGAZ3T24F75HQLIUZSDSMYPEOXCPFA"
PASSWORD_LENGTH = 25 PASSWORD_LENGTH = 25
ONIONR_TAGLINE = 'Private P2P Communication - GPLv3 - https://Onionr.net' 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_CODENAME = 'Taraxacum'
ONIONR_VERSION_TUPLE = tuple(ONIONR_VERSION.split('.')) # (MAJOR, MINOR, VERSION) 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. 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 MIN_PY_VERSION = 7 # min version of 7 so we can take advantage of non-cyclic type hints
DEVELOPMENT_MODE = False 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""" """Onionr user IDs are ed25519 keys, which are always 32 bytes in length"""
MAIN_PUBLIC_KEY_SIZE = 32 MAIN_PUBLIC_KEY_SIZE = 32
ORIG_RUN_DIR_ENV_VAR = 'ORIG_ONIONR_RUN_DIR' ORIG_RUN_DIR_ENV_VAR = 'ORIG_ONIONR_RUN_DIR'
DATABASE_LOCK_TIMEOUT = 60 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' BLOCK_EXPORT_FILE_EXT = '.onionr'
# Begin OnionrValues migrated values # 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): if os.path.exists(filepaths.daemon_mark_file):
SCRIPT_NAME = 'start-daemon.sh' SCRIPT_NAME = 'start-daemon.sh'
else: else:
SCRIPT_NAME = 'onionr.sh' SCRIPT_NAME = 'onionr.sh'
if 'qubes' in platform.release().lower():
IS_QUBES = True

View File

@ -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)

View File

@ -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)

View File

@ -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')

View File

@ -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}')

View File

@ -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')

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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." 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 $(dirname $0)/onionr.sh start & disown
sleep 2 sleep 2
$(dirname $0)/onionr.sh open-home #$(dirname $0)/onionr.sh open-home

View File

@ -1 +0,0 @@
https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/robots.txt,http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/robots.txt,http://rurcblzhmdk22kttfkel2zduhyu3r6to7knyc7wiorzrx5gw4c3lftad.onion/

View File

@ -19,7 +19,6 @@ import onionrblocks
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, '')
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
# import after path insert # import after path insert
from onionrutils.localcommand import local_command
""" """
This program is free software: you can redistribute it and/or modify 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): def on_blocktest_cmd(api, data=None):
bl = onionrblocks.create_anonvdf_block(input("Enter a message:").encode('utf-8'), b"tst", 3600) 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): def on_printtest_cmd(api, data=None):

View File

@ -57,7 +57,10 @@ def on_bootstrap(api, data):
while not config.get('tor.transport_address'): while not config.get('tor.transport_address'):
sleep(1) sleep(1)
config.reload() try:
config.reload()
except Exception:
logger.error(traceback.format_exc(), terminal=True)
socks_address, socks_port = get_socks()[0] socks_address, socks_port = get_socks()[0]

View File

@ -1,5 +1,15 @@
import socks 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: class TorPeer:
@ -18,7 +28,18 @@ class TorPeer:
s = socks.socksocket() s = socks.socksocket()
s.set_proxy(socks.SOCKS4, self.socks_host, self.socks_port, rdns=True) s.set_proxy(socks.SOCKS4, self.socks_host, self.socks_port, rdns=True)
s.settimeout(connect_timeout) 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 return s
def __hash__(self): def __hash__(self):

View File

@ -43,7 +43,7 @@
"minimum_score": -100 "minimum_score": -100
}, },
"plugins": { "plugins": {
"disabled": [], "disabled": ["unixtransport"],
"enabled": [] "enabled": []
}, },
"statistics": { "statistics": {

Binary file not shown.

View File

@ -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)
}
}
})

View File

@ -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>

View File

@ -1,4 +0,0 @@
#friendList button{
display: inline;
margin-right: 10px;
}

View File

@ -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
}
}()

View File

@ -1,13 +0,0 @@
.donateHeader{
font-size: 2em;
}
.donateFinished{
display: block;
margin-top: 1em;
}
.donateBody p{
padding-top: 1em;
text-align: justify;
}

View File

@ -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>

View File

@ -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)
}
}
})
}

View File

@ -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>

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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')

View File

@ -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
}
})

View File

@ -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;
}

View File

@ -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>

View File

@ -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;
});
}
}
})();

View File

@ -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"];
});

View File

@ -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}

File diff suppressed because one or more lines are too long

View File

@ -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();

File diff suppressed because one or more lines are too long

View File

@ -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'
})
})
}

View File

@ -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

View File

@ -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