Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
cb977a2719 | ||
|
e1b4b12024 | ||
|
f435a6e6fa |
12
AUTHORS.MD
Normal file
12
AUTHORS.MD
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Incomplete List of Contributors
|
||||||
|
|
||||||
|
Onionr is created by a team of hard working volunteers.
|
||||||
|
|
||||||
|
In no order of importance, these people make Onionr happen:
|
||||||
|
|
||||||
|
* [Beardog (Kevin Froman)](https://www.chaoswebs.net/) - Project founder, owner and core developer
|
||||||
|
* [InvisaMage](https://invisamage.com/) - Web UI Bulma design
|
||||||
|
* [Arinerron](https://arinerron.com/) - Logger and config modules, testing and other contributions
|
||||||
|
* [Anhar Ismail](https://github.com/anharismail) - Created Onionr's logo
|
||||||
|
|
||||||
|
+ Other contributors and testers
|
@ -1,4 +1,4 @@
|
|||||||
FROM python:3.10
|
FROM python:3.7
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
@ -15,7 +15,7 @@ RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
|
|||||||
locale-gen
|
locale-gen
|
||||||
ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8
|
ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8
|
||||||
|
|
||||||
ADD ./requirements-x86-all-plugins.txt /app/requirements.txt
|
ADD ./requirements.txt /app/requirements.txt
|
||||||
RUN pip3 install --require-hashes -r requirements.txt
|
RUN pip3 install --require-hashes -r requirements.txt
|
||||||
|
|
||||||
#Add Onionr source
|
#Add Onionr source
|
||||||
@ -28,4 +28,4 @@ RUN chmod g=u -R /app
|
|||||||
USER 1000
|
USER 1000
|
||||||
ENV HOME=/app
|
ENV HOME=/app
|
||||||
|
|
||||||
CMD ["bash", "./onionr.sh", "start"]
|
CMD ["bash", "./run-onionr-node.sh"]
|
||||||
|
15
README.md
15
README.md
@ -11,23 +11,23 @@
|
|||||||
WIP anonymous social platform, mail, file sharing and marketplace
|
WIP anonymous social platform, mail, file sharing and marketplace
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<img src="https://img.shields.io/badge/License-aGPLv3-yellow"> <img src='https://img.shields.io/badge/python%20version%20%F0%9F%90%8D-3.10+-blue'>
|
<img src="https://img.shields.io/badge/License-aGPLv3-yellow"> <img src='https://img.shields.io/badge/python%20version%20%F0%9F%90%8D-3.7+-blue'>
|
||||||
|
|
||||||
<a href='https://twitter.com/onionrnet'><img src='https://img.shields.io/twitter/follow/onionrnet?style=social'></a> - [Discord](https://discord.gg/DVF2bEAzrt) - Matrix: #onionr:amorgan.xyz
|
<a href='https://twitter.com/onionrnet'><img src='https://img.shields.io/twitter/follow/onionrnet?style=social'></a> - [Discord](https://discord.gg/DVF2bEAzrt) - Matrix: #onionr:amorgan.xyz
|
||||||
|
|
||||||
| | | |
|
| | | |
|
||||||
| ----------- | ----------- | ----------- |
|
| ----------- | ----------- | ----------- |
|
||||||
| [Install](#install-and-run-on-linux) | [Features](#main-features) | [Screenshots](#screenshots)|
|
| [Install](#install-and-run-on-linux) | [Features](#main-features) | [Screenshots](#screenshots)|
|
||||||
| [Docs](#documentation) | [Get involved](#help-out) | [Onionr.net](https://onionr.net/)/[.onion](http://onionrbak72t5zhbzuey2fdkpczlvhowgcpqc6uoyrd3uxztzxwz5cyd.onion/) |
|
| [Docs](#documentation)/[web copy](https://beardog108.github.io/onionr/) | [Get involved](#help-out) | [Onionr.net](https://onionr.net/)/[.onion](http://onionrbak72t5zhbzuey2fdkpczlvhowgcpqc6uoyrd3uxztzxwz5cyd.onion/) |
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**The main repository for this software is at https://gitlab.com/beardog/onionr/**
|
**The main repository for this software is at https://git.VoidNet.tech/kev/onionr/**
|
||||||
|
|
||||||
***Note that this README reflects the state of the rewrite, and not the original alpha network***
|
***Note that this README reflects the state of the rewrite, and not the original alpha network***
|
||||||
|
|
||||||
Mirrors [Gitea](https://git.voidnet.tech/kev/onionr)
|
Mirrors [Gitlab](https://gitlab.com/beardog/onionr)
|
||||||
|
|
||||||
|
|
||||||
Onionr ("Onion Relay") is a decentralized/distributed peer-to-peer communication network, designed to be anonymous and resistant to (meta)data analysis, spam, and corruption.
|
Onionr ("Onion Relay") is a decentralized/distributed peer-to-peer communication network, designed to be anonymous and resistant to (meta)data analysis, spam, and corruption.
|
||||||
@ -84,13 +84,14 @@ Master may be unstable, you should use the latest release tag. (checkout via git
|
|||||||
|
|
||||||
`$ sudo apt install python3-pip python3-dev tor`
|
`$ sudo apt install python3-pip python3-dev tor`
|
||||||
|
|
||||||
* Have python3.10, python3-pip, Tor (daemon, not browser) installed. python3-dev is recommended.
|
* Have python3.7+, python3-pip, Tor (daemon, not browser) installed. python3-dev is recommended.
|
||||||
* You may need build-essentials or the equivalent of your platform
|
* You may need build-essentials or the equivalent of your platform
|
||||||
* Clone the git repo: `$ git clone https://gitlab.com/beardog/onionr --tags`
|
* Clone the git repo: `$ git clone https://gitlab.com/beardog/onionr --tags`
|
||||||
* cd into install direction: `$ cd onionr/`
|
* cd into install direction: `$ cd onionr/`
|
||||||
* Install the Python dependencies ([virtualenv strongly recommended](https://virtualenv.pypa.io/en/stable/userguide/)): `$ pip3 install --require-hashes -r requirements-x86-all-plugins.txt`
|
* Install the Python dependencies ([virtualenv strongly recommended](https://virtualenv.pypa.io/en/stable/userguide/)): `$ pip3 install --require-hashes -r requirements.txt` (on ARM64 devices like Raspberry Pi 4's use requirements-ARM.txt instead.)
|
||||||
|
* (Optional): Install desktop notification dependencies: `$ pip3 install --require-hashes -r requirements-notifications.txt`
|
||||||
|
|
||||||
Require-hashes is suggested for supply-chain security but is optional. The hashes are not correct for ARM machines. If you are just running a node or want a bare-bones install you can use requirements-base-x86.txt and selectively install the requirements.txt files in static-data/official-plugins/ subdirectories
|
(--require-hashes is intended to prevent exploitation via compromise of PyPi/CA certificates)
|
||||||
|
|
||||||
## Run Onionr
|
## Run Onionr
|
||||||
|
|
||||||
|
20
ROADMAP.md
20
ROADMAP.md
@ -6,32 +6,32 @@
|
|||||||
|
|
||||||
* [ X ] Implement new block format with verifiable delay function
|
* [ X ] Implement new block format with verifiable delay function
|
||||||
|
|
||||||
* [ X ] Implement overhauled gossip system with dandelion++
|
* [ ] Implement overhauled gossip system with dandelion++
|
||||||
|
|
||||||
The new system is separated from the underlying networks used and makes it much easier to implement new transport methods. Dandelion++ is also better than the old block mixing we did.
|
The new system is separated from the underlying networks used and makes it much easier to implement new transport methods. Dandelion++ is also better than the old block mixing we did.
|
||||||
|
|
||||||
|
* [ ] Revamped key/encrypted messaging (encrypted blocks)
|
||||||
|
* [ ] Restore webUI as a plugin
|
||||||
|
* [ ] Restore static site/file sharing plugin
|
||||||
|
* [ ] Restore/reimplement mail plugin
|
||||||
|
* [ ] Restore/reimplement friends plugin
|
||||||
|
* [ ] Refresh test suite
|
||||||
|
|
||||||
|
|
||||||
## Misc
|
## Misc
|
||||||
|
|
||||||
* [ ] Spruce up documentation
|
* [ ] Spruce up documentation
|
||||||
* [ ] Restore LAN transport
|
* [ ] Restore LAN transport
|
||||||
* [ ] Restore webUI as a plugin
|
|
||||||
* [ ] Restore static site/file sharing plugin
|
|
||||||
* [ ] Restore/reimplement mail plugin
|
|
||||||
* [ ] Restore/reimplement friends plugin
|
|
||||||
* [ ] Refresh test suite
|
|
||||||
* [ ] Revamped key/encrypted messaging (encrypted blocks)
|
|
||||||
|
|
||||||
|
|
||||||
|
## Web of trust release (10.0)
|
||||||
## Web of trust release (~10.0)
|
|
||||||
|
|
||||||
To facilitate the below market plugin/application, Onionr will need a web of trust system.
|
To facilitate the below market plugin/application, Onionr will need a web of trust system.
|
||||||
|
|
||||||
* [ ] Web of trust plugin or module
|
* [ ] Web of trust plugin or module
|
||||||
|
|
||||||
|
|
||||||
## Market Plugin Release (~10.1)
|
## Market Plugin Release (10.1)
|
||||||
|
|
||||||
The Onionr team believes the Monero community is currently lacking a good p2p market, and as such we hope to build a solution using Onionr as a transport. This may be a separate project and as opposed to a plugin.
|
The Onionr team believes the Monero community is currently lacking a good p2p market, and as such we hope to build a solution using Onionr as a transport. This may be a separate project and as opposed to a plugin.
|
||||||
|
|
||||||
|
124
docs/dev/http-api.md
Executable file
124
docs/dev/http-api.md
Executable file
@ -0,0 +1,124 @@
|
|||||||
|
# Onionr HTTP API
|
||||||
|
|
||||||
|
All HTTP interfaces in the Onionr reference client use the [Flask](http://flask.pocoo.org/) web framework with the [gevent](http://www.gevent.org/) WSGI server.
|
||||||
|
|
||||||
|
## Client & Public difference
|
||||||
|
|
||||||
|
The client API server is a locked down interface intended for authenticated local communication.
|
||||||
|
|
||||||
|
The public API server is available only remotely from Tor & I2P. It is the interface in which peers use to communicate with one another.
|
||||||
|
|
||||||
|
# Client API
|
||||||
|
|
||||||
|
Please note: endpoints that simply provide static web app files are not documented here.
|
||||||
|
|
||||||
|
* /serviceactive/pubkey
|
||||||
|
- Methods: GET
|
||||||
|
- Returns true or false based on if a given public key has an active direct connection service.
|
||||||
|
* /queueResponseAdd/key (DEPRECATED)
|
||||||
|
- Methods: POST
|
||||||
|
- Accepts form key 'data' to set queue response information from a plugin
|
||||||
|
- Returns success if no error occurs
|
||||||
|
* /queueResponse/key (DEPRECATED)
|
||||||
|
- Methods: GET
|
||||||
|
- Returns the queue response for a key. Returns failure with a 404 code if a code is not set.
|
||||||
|
* /ping
|
||||||
|
- Methods: GET
|
||||||
|
- Returns "pong!"
|
||||||
|
* /getblocksbytype/type
|
||||||
|
- Methods: GET
|
||||||
|
- Returns a list of stored blocks by a given type
|
||||||
|
* /getblockbody/hash
|
||||||
|
- Methods: GET
|
||||||
|
- Returns the main data section of a block
|
||||||
|
* /getblockdata/hash
|
||||||
|
- Methods: GET
|
||||||
|
- Returns the entire data contents of a block, including metadata.
|
||||||
|
* /getblockheader/hash
|
||||||
|
- Methods: GET
|
||||||
|
- Returns the header (metadata section) of a block.
|
||||||
|
* /gethidden/
|
||||||
|
- Methods: GET
|
||||||
|
- Returns line separated list of hidden blocks
|
||||||
|
* /hitcount
|
||||||
|
- Methods: GET
|
||||||
|
- Return the amount of requests the public api server has received this session
|
||||||
|
* /lastconnect
|
||||||
|
- Methods: GET
|
||||||
|
- Returns the epoch timestamp of when the last incoming connection to the public API server was logged
|
||||||
|
* /site/hash
|
||||||
|
- Methods: GET
|
||||||
|
- Returns HTML content out of a block
|
||||||
|
* /waitforshare/hash
|
||||||
|
- Methods: POST
|
||||||
|
- Prevents the public API server from listing or sharing a block until it has been uploaded to at least 1 peer.
|
||||||
|
* /shutdown
|
||||||
|
- Methods: GET
|
||||||
|
- Shutdown Onionr. You should probably use /shutdownclean instead.
|
||||||
|
* /shutdownclean
|
||||||
|
- Methods: GET
|
||||||
|
- Tells the communicator daemon to shutdown Onionr. Slower but cleaner.
|
||||||
|
* /getstats
|
||||||
|
- Methods: GET
|
||||||
|
- Returns some JSON serialized statistics
|
||||||
|
* /getuptime
|
||||||
|
- Methods: GET
|
||||||
|
- Returns uptime in seconds
|
||||||
|
* /getActivePubkey
|
||||||
|
- Methods: GET
|
||||||
|
- Returns the current active public key in base32 format
|
||||||
|
* /getHumanReadable/pubkey
|
||||||
|
- Methods: GET
|
||||||
|
- Echos the specified public key in mnemonic format
|
||||||
|
* /insertblock
|
||||||
|
- Methods: POST
|
||||||
|
- Accepts JSON data for creating a new block. 'message' contains the block data, 'to' specifies the peer's public key to encrypt the data to, 'sign' is a boolean for signing the message.
|
||||||
|
* /torready
|
||||||
|
- Methods: POST
|
||||||
|
- Returns boolean if Tor is started or not
|
||||||
|
|
||||||
|
# Public API
|
||||||
|
|
||||||
|
v0
|
||||||
|
|
||||||
|
* /
|
||||||
|
- Methods: GET
|
||||||
|
- Returns a basic HTML informational banner describing Onionr.
|
||||||
|
* /getblocklist
|
||||||
|
- Methods: GET
|
||||||
|
- URI Parameters:
|
||||||
|
- date: unix epoch timestamp for offset
|
||||||
|
- Returns a list of block hashes stored on the node since an offset (all blocks if no timestamp is specified)
|
||||||
|
* /getdata/block-hash
|
||||||
|
- Methods: GET
|
||||||
|
- Returns data for a block based on a provided hash
|
||||||
|
* /www/file-path
|
||||||
|
- Methods: GET
|
||||||
|
- Returns file data. Intended for manually sharing file data directly from an Onionr node.
|
||||||
|
* /ping
|
||||||
|
- Methods: GET
|
||||||
|
- Returns 'pong!'
|
||||||
|
* /pex
|
||||||
|
- Methods: GET
|
||||||
|
- Returns a list of peer addresses reached within recent time
|
||||||
|
* /announce
|
||||||
|
- Methods: POST
|
||||||
|
- Accepts form data for 'node' (valid node address) and 'random' which is a nonce when hashed (blake2b_256) in the format `hash(peerAddress+serverAddress+nonce)`, begins with at least 5 zeros.
|
||||||
|
- Returns 200 with 'Success' if no error occurs. If the post is invalid, 'failure' with code 406 is returned.
|
||||||
|
* /upload
|
||||||
|
- Methods: POST
|
||||||
|
- Accepts form data for 'block' as a 'file' upload.
|
||||||
|
- Returns 200 with 'success' if no error occurs. If the block cannot be accepted, 'failure' with 400 is returned.
|
||||||
|
|
||||||
|
# Direct Connection API
|
||||||
|
|
||||||
|
These are constant endpoints available on direct connection servers. Plugin endpoints for direct connections are not documented here.
|
||||||
|
|
||||||
|
* /ping
|
||||||
|
- Methods: GET
|
||||||
|
- Returns 200 with 'pong!'
|
||||||
|
|
||||||
|
* /close
|
||||||
|
- Methods: GET
|
||||||
|
- Kills the direct connection server, destroying the onion address.
|
||||||
|
- Returns 200 with 'goodbye'
|
@ -2,8 +2,6 @@
|
|||||||
ORIG_ONIONR_RUN_DIR=`pwd`
|
ORIG_ONIONR_RUN_DIR=`pwd`
|
||||||
export ORIG_ONIONR_RUN_DIR
|
export ORIG_ONIONR_RUN_DIR
|
||||||
export PYTHONDONTWRITEBYTECODE=1
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
export PYTHONUNBUFFERED=1
|
|
||||||
export PYTHONOPTIMIZE="true"
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
cd src
|
cd src
|
||||||
./__init__.py "$@"
|
./__init__.py "$@"
|
392
requirements-ARM.txt
Normal file
392
requirements-ARM.txt
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile
|
||||||
|
# To update, run:
|
||||||
|
#
|
||||||
|
# pip-compile --generate-hashes --output-file=requirements-ARM.txt requirements.in
|
||||||
|
#
|
||||||
|
--extra-index-url https://www.piwheels.org/simple
|
||||||
|
|
||||||
|
certifi==2020.12.5 \
|
||||||
|
--hash=sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c \
|
||||||
|
--hash=sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830
|
||||||
|
# via requests
|
||||||
|
cffi==1.14.4 \
|
||||||
|
--hash=sha256:00a1ba5e2e95684448de9b89888ccd02c98d512064b4cb987d48f4b40aa0421e \
|
||||||
|
--hash=sha256:00e28066507bfc3fe865a31f325c8391a1ac2916219340f87dfad602c3e48e5d \
|
||||||
|
--hash=sha256:045d792900a75e8b1e1b0ab6787dd733a8190ffcf80e8c8ceb2fb10a29ff238a \
|
||||||
|
--hash=sha256:0638c3ae1a0edfb77c6765d487fee624d2b1ee1bdfeffc1f0b58c64d149e7eec \
|
||||||
|
--hash=sha256:105abaf8a6075dc96c1fe5ae7aae073f4696f2905fde6aeada4c9d2926752362 \
|
||||||
|
--hash=sha256:155136b51fd733fa94e1c2ea5211dcd4c8879869008fc811648f16541bf99668 \
|
||||||
|
--hash=sha256:1a465cbe98a7fd391d47dce4b8f7e5b921e6cd805ef421d04f5f66ba8f06086c \
|
||||||
|
--hash=sha256:1d2c4994f515e5b485fd6d3a73d05526aa0fcf248eb135996b088d25dfa1865b \
|
||||||
|
--hash=sha256:2c24d61263f511551f740d1a065eb0212db1dbbbbd241db758f5244281590c06 \
|
||||||
|
--hash=sha256:51a8b381b16ddd370178a65360ebe15fbc1c71cf6f584613a7ea08bfad946698 \
|
||||||
|
--hash=sha256:594234691ac0e9b770aee9fcdb8fa02c22e43e5c619456efd0d6c2bf276f3eb2 \
|
||||||
|
--hash=sha256:5cf4be6c304ad0b6602f5c4e90e2f59b47653ac1ed9c662ed379fe48a8f26b0c \
|
||||||
|
--hash=sha256:64081b3f8f6f3c3de6191ec89d7dc6c86a8a43911f7ecb422c60e90c70be41c7 \
|
||||||
|
--hash=sha256:6bc25fc545a6b3d57b5f8618e59fc13d3a3a68431e8ca5fd4c13241cd70d0009 \
|
||||||
|
--hash=sha256:798caa2a2384b1cbe8a2a139d80734c9db54f9cc155c99d7cc92441a23871c03 \
|
||||||
|
--hash=sha256:7c6b1dece89874d9541fc974917b631406233ea0440d0bdfbb8e03bf39a49b3b \
|
||||||
|
--hash=sha256:840793c68105fe031f34d6a086eaea153a0cd5c491cde82a74b420edd0a2b909 \
|
||||||
|
--hash=sha256:8d6603078baf4e11edc4168a514c5ce5b3ba6e3e9c374298cb88437957960a53 \
|
||||||
|
--hash=sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35 \
|
||||||
|
--hash=sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26 \
|
||||||
|
--hash=sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b \
|
||||||
|
--hash=sha256:a5ed8c05548b54b998b9498753fb9cadbfd92ee88e884641377d8a8b291bcc01 \
|
||||||
|
--hash=sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb \
|
||||||
|
--hash=sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293 \
|
||||||
|
--hash=sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd \
|
||||||
|
--hash=sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d \
|
||||||
|
--hash=sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3 \
|
||||||
|
--hash=sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d \
|
||||||
|
--hash=sha256:d5ff0621c88ce83a28a10d2ce719b2ee85635e85c515f12bac99a95306da4b2e \
|
||||||
|
--hash=sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca \
|
||||||
|
--hash=sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d \
|
||||||
|
--hash=sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775 \
|
||||||
|
--hash=sha256:ec80dc47f54e6e9a78181ce05feb71a0353854cc26999db963695f950b5fb375 \
|
||||||
|
--hash=sha256:f032b34669220030f905152045dfa27741ce1a6db3324a5bc0b96b6c7420c87b \
|
||||||
|
--hash=sha256:f60567825f791c6f8a592f3c6e3bd93dd2934e3f9dac189308426bd76b00ef3b \
|
||||||
|
--hash=sha256:f803eaa94c2fcda012c047e62bc7a51b0bdabda1cad7a92a522694ea2d76e49f
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# pynacl
|
||||||
|
chardet==4.0.0 \
|
||||||
|
--hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \
|
||||||
|
--hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5
|
||||||
|
# via requests
|
||||||
|
click==7.1.2 \
|
||||||
|
--hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a \
|
||||||
|
--hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc
|
||||||
|
# via flask
|
||||||
|
deadsimplekv==0.3.2 \
|
||||||
|
--hash=sha256:a725f4a9d1156ebb66b7535ac150006881e0365b715e34e3709214827b8b0c4c \
|
||||||
|
--hash=sha256:df00262d26c3dcfecb710425a7413059480d8cf026216042d7cbffb8514818b2
|
||||||
|
# via -r requirements.in
|
||||||
|
filenuke==0.0.0 \
|
||||||
|
--hash=sha256:147011c0125121469cae0a8a7f4df399f470e54aa29a08f2d2c099bf0118dcee \
|
||||||
|
--hash=sha256:c55535dcecfdb27c5f4ce664d46e115950b5429763b5db75c198053646177f8f
|
||||||
|
# via -r requirements.in
|
||||||
|
flask==1.1.2 \
|
||||||
|
--hash=sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060 \
|
||||||
|
--hash=sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557
|
||||||
|
# via -r requirements.in
|
||||||
|
gevent==20.9.0 \
|
||||||
|
--hash=sha256:10110d4881aec04f218c316cb796b18c8b2cac67ae0eb5b0c5780056757268a2 \
|
||||||
|
--hash=sha256:1628a403fc9c3ea9b35924638a4d4fbe236f60ecdf4e22ed133fbbaf0bc7cb6b \
|
||||||
|
--hash=sha256:1cfa3674866294623e324fa5b76eba7b96744d1956a605cfe24d26c5cd890f91 \
|
||||||
|
--hash=sha256:2269574444113cb4ca1c1808ab9460a87fe25e1c34a6e36d975d4af46e4afff9 \
|
||||||
|
--hash=sha256:283a021a2e14adfad718346f18982b80569d9c3a59e97cfae1b7d4c5b017941a \
|
||||||
|
--hash=sha256:2aa70726ad1883fe7c17774e5ccc91ac6e30334efa29bafb9b8fe8ca6091b219 \
|
||||||
|
--hash=sha256:315a63a35068183dfb9bc0331c7bb3c265ee7db8a11797cbe98dadbdb45b5d35 \
|
||||||
|
--hash=sha256:324808a8558c733f7a9734525483795d52ca3bbd5662b24b361d81c075414b1f \
|
||||||
|
--hash=sha256:33a63f230755c6813fca39d9cea2a8894df32df2ee58fd69d8bf8fcc1d8e018e \
|
||||||
|
--hash=sha256:5f6d48051d336561ec08995431ee4d265ac723a64bba99cc58c3eb1a4d4f5c8d \
|
||||||
|
--hash=sha256:8d338cd6d040fe2607e5305dd7991b5960b3780ae01f804c2ac5760d31d3b2c6 \
|
||||||
|
--hash=sha256:906175e3fb25f377a0b581e79d3ed5a7d925c136ff92fd022bb3013e25f5f3a9 \
|
||||||
|
--hash=sha256:93980e51dd2e5f81899d644a0b6ef4a73008c679fcedd50e3b21cc3451ba2424 \
|
||||||
|
--hash=sha256:9bb477f514cf39dc20651b479bf1ad4f38b9a679be2bfa3e162ec0c3785dfa2a \
|
||||||
|
--hash=sha256:a8733a01974433d91308f8c44fa6cc13428b15bb39d46540657e260ff8852cb1 \
|
||||||
|
--hash=sha256:adbb267067f56696b2babced3d0856aa39dcf14b8ccd2dffa1fab587b00c6f80 \
|
||||||
|
--hash=sha256:afc177c37de41ce9c27d351ac84cbaf34407effcab5d6641645838f39d365be1 \
|
||||||
|
--hash=sha256:b07fcbca3e819296979d82fac3d8b44f0d5ced57b9a04dffcfd194da99c8eb2d \
|
||||||
|
--hash=sha256:b2948566003a1030e47507755fe1f446995e8671c0c67571091539e01faf94cc \
|
||||||
|
--hash=sha256:db208e74a32cff7f55f5aa1ba5d7d1c1a086a6325c8702ae78a5c741155552ff \
|
||||||
|
--hash=sha256:dd4c6b2f540b25c3d0f277a725bc1a900ce30a681b90a081216e31f814be453b \
|
||||||
|
--hash=sha256:e11de4b4d107ca2f35000eb08e9c4c4621c153103b400f48a9ea95b96d8c7e0b \
|
||||||
|
--hash=sha256:eba19bae532d0c48d489fa16815b242ce074b1f4b63e8a8e663232cbe311ead9 \
|
||||||
|
--hash=sha256:fb33dc1ab27557bccd64ad4bf81e68c8b0d780fe937b1e2c0814558798137229
|
||||||
|
# via -r requirements.in
|
||||||
|
greenlet==1.0.0 \
|
||||||
|
--hash=sha256:0a77691f0080c9da8dfc81e23f4e3cffa5accf0f5b56478951016d7cfead9196 \
|
||||||
|
--hash=sha256:0ddd77586553e3daf439aa88b6642c5f252f7ef79a39271c25b1d4bf1b7cbb85 \
|
||||||
|
--hash=sha256:111cfd92d78f2af0bc7317452bd93a477128af6327332ebf3c2be7df99566683 \
|
||||||
|
--hash=sha256:122c63ba795fdba4fc19c744df6277d9cfd913ed53d1a286f12189a0265316dd \
|
||||||
|
--hash=sha256:181300f826625b7fd1182205b830642926f52bd8cdb08b34574c9d5b2b1813f7 \
|
||||||
|
--hash=sha256:1a1ada42a1fd2607d232ae11a7b3195735edaa49ea787a6d9e6a53afaf6f3476 \
|
||||||
|
--hash=sha256:1bb80c71de788b36cefb0c3bb6bfab306ba75073dbde2829c858dc3ad70f867c \
|
||||||
|
--hash=sha256:1d1d4473ecb1c1d31ce8fd8d91e4da1b1f64d425c1dc965edc4ed2a63cfa67b2 \
|
||||||
|
--hash=sha256:292e801fcb3a0b3a12d8c603c7cf340659ea27fd73c98683e75800d9fd8f704c \
|
||||||
|
--hash=sha256:2c65320774a8cd5fdb6e117c13afa91c4707548282464a18cf80243cf976b3e6 \
|
||||||
|
--hash=sha256:4365eccd68e72564c776418c53ce3c5af402bc526fe0653722bc89efd85bf12d \
|
||||||
|
--hash=sha256:5352c15c1d91d22902582e891f27728d8dac3bd5e0ee565b6a9f575355e6d92f \
|
||||||
|
--hash=sha256:58ca0f078d1c135ecf1879d50711f925ee238fe773dfe44e206d7d126f5bc664 \
|
||||||
|
--hash=sha256:5d4030b04061fdf4cbc446008e238e44936d77a04b2b32f804688ad64197953c \
|
||||||
|
--hash=sha256:5d69bbd9547d3bc49f8a545db7a0bd69f407badd2ff0f6e1a163680b5841d2b0 \
|
||||||
|
--hash=sha256:5f297cb343114b33a13755032ecf7109b07b9a0020e841d1c3cedff6602cc139 \
|
||||||
|
--hash=sha256:62afad6e5fd70f34d773ffcbb7c22657e1d46d7fd7c95a43361de979f0a45aef \
|
||||||
|
--hash=sha256:647ba1df86d025f5a34043451d7c4a9f05f240bee06277a524daad11f997d1e7 \
|
||||||
|
--hash=sha256:719e169c79255816cdcf6dccd9ed2d089a72a9f6c42273aae12d55e8d35bdcf8 \
|
||||||
|
--hash=sha256:7cd5a237f241f2764324396e06298b5dee0df580cf06ef4ada0ff9bff851286c \
|
||||||
|
--hash=sha256:875d4c60a6299f55df1c3bb870ebe6dcb7db28c165ab9ea6cdc5d5af36bb33ce \
|
||||||
|
--hash=sha256:90b6a25841488cf2cb1c8623a53e6879573010a669455046df5f029d93db51b7 \
|
||||||
|
--hash=sha256:94620ed996a7632723a424bccb84b07e7b861ab7bb06a5aeb041c111dd723d36 \
|
||||||
|
--hash=sha256:b5f1b333015d53d4b381745f5de842f19fe59728b65f0fbb662dafbe2018c3a5 \
|
||||||
|
--hash=sha256:c5b22b31c947ad8b6964d4ed66776bcae986f73669ba50620162ba7c832a6b6a \
|
||||||
|
--hash=sha256:c93d1a71c3fe222308939b2e516c07f35a849c5047f0197442a4d6fbcb4128ee \
|
||||||
|
--hash=sha256:cdb90267650c1edb54459cdb51dab865f6c6594c3a47ebd441bc493360c7af70 \
|
||||||
|
--hash=sha256:cfd06e0f0cc8db2a854137bd79154b61ecd940dce96fad0cba23fe31de0b793c \
|
||||||
|
--hash=sha256:d3789c1c394944084b5e57c192889985a9f23bd985f6d15728c745d380318128 \
|
||||||
|
--hash=sha256:da7d09ad0f24270b20f77d56934e196e982af0d0a2446120cb772be4e060e1a2 \
|
||||||
|
--hash=sha256:df3e83323268594fa9755480a442cabfe8d82b21aba815a71acf1bb6c1776218 \
|
||||||
|
--hash=sha256:df8053867c831b2643b2c489fe1d62049a98566b1646b194cc815f13e27b90df \
|
||||||
|
--hash=sha256:e1128e022d8dce375362e063754e129750323b67454cac5600008aad9f54139e \
|
||||||
|
--hash=sha256:e6e9fdaf6c90d02b95e6b0709aeb1aba5affbbb9ccaea5502f8638e4323206be \
|
||||||
|
--hash=sha256:eac8803c9ad1817ce3d8d15d1bb82c2da3feda6bee1153eec5c58fa6e5d3f770 \
|
||||||
|
--hash=sha256:eb333b90036358a0e2c57373f72e7648d7207b76ef0bd00a4f7daad1f79f5203 \
|
||||||
|
--hash=sha256:ed1d1351f05e795a527abc04a0d82e9aecd3bdf9f46662c36ff47b0b00ecaf06 \
|
||||||
|
--hash=sha256:f3dc68272990849132d6698f7dc6df2ab62a88b0d36e54702a8fd16c0490e44f \
|
||||||
|
--hash=sha256:f59eded163d9752fd49978e0bab7a1ff21b1b8d25c05f0995d140cc08ac83379 \
|
||||||
|
--hash=sha256:f5e2d36c86c7b03c94b8459c3bd2c9fe2c7dab4b258b8885617d44a22e453fb7 \
|
||||||
|
--hash=sha256:f6f65bf54215e4ebf6b01e4bb94c49180a589573df643735107056f7a910275b \
|
||||||
|
--hash=sha256:f8450d5ef759dbe59f84f2c9f77491bb3d3c44bc1a573746daf086e70b14c243 \
|
||||||
|
--hash=sha256:f97d83049715fd9dec7911860ecf0e17b48d8725de01e45de07d8ac0bd5bc378
|
||||||
|
# via gevent
|
||||||
|
idna==2.10 \
|
||||||
|
--hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \
|
||||||
|
--hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0
|
||||||
|
# via requests
|
||||||
|
itsdangerous==1.1.0 \
|
||||||
|
--hash=sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19 \
|
||||||
|
--hash=sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749
|
||||||
|
# via flask
|
||||||
|
jinja2==2.11.2 \
|
||||||
|
--hash=sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0 \
|
||||||
|
--hash=sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035
|
||||||
|
# via flask
|
||||||
|
markupsafe==1.1.1 \
|
||||||
|
--hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \
|
||||||
|
--hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \
|
||||||
|
--hash=sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235 \
|
||||||
|
--hash=sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5 \
|
||||||
|
--hash=sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42 \
|
||||||
|
--hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \
|
||||||
|
--hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b \
|
||||||
|
--hash=sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1 \
|
||||||
|
--hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \
|
||||||
|
--hash=sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183 \
|
||||||
|
--hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \
|
||||||
|
--hash=sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b \
|
||||||
|
--hash=sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1 \
|
||||||
|
--hash=sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15 \
|
||||||
|
--hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \
|
||||||
|
--hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \
|
||||||
|
--hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \
|
||||||
|
--hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \
|
||||||
|
--hash=sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735 \
|
||||||
|
--hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \
|
||||||
|
--hash=sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e \
|
||||||
|
--hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \
|
||||||
|
--hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \
|
||||||
|
--hash=sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21 \
|
||||||
|
--hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \
|
||||||
|
--hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \
|
||||||
|
--hash=sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b \
|
||||||
|
--hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \
|
||||||
|
--hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \
|
||||||
|
--hash=sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f \
|
||||||
|
--hash=sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2 \
|
||||||
|
--hash=sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7 \
|
||||||
|
--hash=sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be
|
||||||
|
# via jinja2
|
||||||
|
niceware==0.2.1 \
|
||||||
|
--hash=sha256:0f8b192f2a1e800e068474f6e208be9c7e2857664b33a96f4045340de4e5c69c \
|
||||||
|
--hash=sha256:cf2dc0e1567d36d067c61b32fed0f1b9c4534ed511f9eeead4ba548d03b5c9eb
|
||||||
|
# via -r requirements.in
|
||||||
|
psutil==5.8.0 \
|
||||||
|
--hash=sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64 \
|
||||||
|
--hash=sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131 \
|
||||||
|
--hash=sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c \
|
||||||
|
--hash=sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6 \
|
||||||
|
--hash=sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023 \
|
||||||
|
--hash=sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df \
|
||||||
|
--hash=sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394 \
|
||||||
|
--hash=sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4 \
|
||||||
|
--hash=sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b \
|
||||||
|
--hash=sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2 \
|
||||||
|
--hash=sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d \
|
||||||
|
--hash=sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65 \
|
||||||
|
--hash=sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d \
|
||||||
|
--hash=sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef \
|
||||||
|
--hash=sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7 \
|
||||||
|
--hash=sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60 \
|
||||||
|
--hash=sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6 \
|
||||||
|
--hash=sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8 \
|
||||||
|
--hash=sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b \
|
||||||
|
--hash=sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d \
|
||||||
|
--hash=sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac \
|
||||||
|
--hash=sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935 \
|
||||||
|
--hash=sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d \
|
||||||
|
--hash=sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28 \
|
||||||
|
--hash=sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876 \
|
||||||
|
--hash=sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0 \
|
||||||
|
--hash=sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3 \
|
||||||
|
--hash=sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563
|
||||||
|
# via -r requirements.in
|
||||||
|
pycparser==2.20 \
|
||||||
|
--hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \
|
||||||
|
--hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705
|
||||||
|
# via cffi
|
||||||
|
pynacl==1.4.0 \
|
||||||
|
--hash=sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4 \
|
||||||
|
--hash=sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4 \
|
||||||
|
--hash=sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574 \
|
||||||
|
--hash=sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d \
|
||||||
|
--hash=sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634 \
|
||||||
|
--hash=sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25 \
|
||||||
|
--hash=sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f \
|
||||||
|
--hash=sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505 \
|
||||||
|
--hash=sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122 \
|
||||||
|
--hash=sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7 \
|
||||||
|
--hash=sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420 \
|
||||||
|
--hash=sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f \
|
||||||
|
--hash=sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96 \
|
||||||
|
--hash=sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6 \
|
||||||
|
--hash=sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6 \
|
||||||
|
--hash=sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514 \
|
||||||
|
--hash=sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff \
|
||||||
|
--hash=sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80
|
||||||
|
# via -r requirements.in
|
||||||
|
pysocks==1.7.1 \
|
||||||
|
--hash=sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299 \
|
||||||
|
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
|
||||||
|
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
||||||
|
# via -r requirements.in
|
||||||
|
requests==2.25.1 \
|
||||||
|
--hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
|
||||||
|
--hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# streamedrequests
|
||||||
|
six==1.15.0 \
|
||||||
|
--hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \
|
||||||
|
--hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced
|
||||||
|
# via pynacl
|
||||||
|
stem==1.8.0 \
|
||||||
|
--hash=sha256:a0b48ea6224e95f22aa34c0bc3415f0eb4667ddeae3dfb5e32a6920c185568c2
|
||||||
|
# via -r requirements.in
|
||||||
|
streamedrequests==1.0.3 \
|
||||||
|
--hash=sha256:4388ffc0ee94dda719dafc4324b8ddd108cb2231ec59871de79e2592bf4eef0a \
|
||||||
|
--hash=sha256:ee68417a1522e75c35b1b2d5f3b6f7e76a3a1a6c0ef5e0c573d08307910079d8
|
||||||
|
# via -r requirements.in
|
||||||
|
toomanyobjs==1.1.0 \
|
||||||
|
--hash=sha256:99e27468f9dad19127be9e2fb086b42acd69aed9ad7e63cef74d6e4389be0534
|
||||||
|
# via -r requirements.in
|
||||||
|
ujson==4.0.1 \
|
||||||
|
--hash=sha256:078808c385036cba73cad96f498310c61e9b5ae5ac9ea01e7c3996ece544b556 \
|
||||||
|
--hash=sha256:0a2e1b211714eb1ec0772a013ec9967f8f95f21c84e8f46382e9f8a32ae781fe \
|
||||||
|
--hash=sha256:0f412c3f59b1ab0f40018235224ca0cf29232d0201ff5085618565a8a9c810ed \
|
||||||
|
--hash=sha256:26cf6241b36ff5ce4539ae687b6b02673109c5e3efc96148806a7873eaa229d3 \
|
||||||
|
--hash=sha256:2b2d9264ac76aeb11f590f7a1ccff0689ba1313adacbb6d38d3b15f21a392897 \
|
||||||
|
--hash=sha256:4f12b0b4e235b35d49f15227b0a827e614c52dda903c58a8f5523936c233dfc7 \
|
||||||
|
--hash=sha256:4fe8c6112b732cba5a722f7cbe22f18d405f6f44415794a5b46473a477635233 \
|
||||||
|
--hash=sha256:51480048373cf97a6b97fcd70c3586ca0a31f27e22ab680fb14c1f22bedbf743 \
|
||||||
|
--hash=sha256:568bb3e7f035006147af4ce3a9ced7d126c92e1a8607c7b2266007b1c1162c53 \
|
||||||
|
--hash=sha256:5fe1536465b1c86e32a47113abd3178001b7c2dcd61f95f336fe2febf4661e74 \
|
||||||
|
--hash=sha256:71703a269f074ff65b9d7746662e4b3e76a4af443e532218af1e8ce15d9b1e7b \
|
||||||
|
--hash=sha256:7a1545ac2476db4cc1f0f236603ccbb50991fc1bba480cda1bc06348cc2a2bf0 \
|
||||||
|
--hash=sha256:a5200a68f1dcf3ce275e1cefbcfa3914b70c2b5e2f71c2e31556aa1f7244c845 \
|
||||||
|
--hash=sha256:a618af22407baeadb3f046f81e7a5ee5e9f8b0b716d2b564f92276a54d26a823 \
|
||||||
|
--hash=sha256:a79bca47eafb31c74b38e68623bc9b2bb930cb48fab1af31c8f2cb68cf473421 \
|
||||||
|
--hash=sha256:b87379a3f8046d6d111762d81f3384bf38ab24b1535c841fe867a4a097d84523 \
|
||||||
|
--hash=sha256:bd4c77aee3ffb920e2dbc21a9e0c7945a400557ce671cfd57dbd569f5ebc619d \
|
||||||
|
--hash=sha256:c354c1617b0a4378b6279d0cd511b769500cf3fa7c42e8e004cbbbb6b4c2a875 \
|
||||||
|
--hash=sha256:c604024bd853b5df6be7d933e934da8dd139e6159564db7c55b92a9937678093 \
|
||||||
|
--hash=sha256:e7ab24942b2d57920d75b817b8eead293026db003247e26f99506bdad86c61b4 \
|
||||||
|
--hash=sha256:f8a60928737a9a47e692fcd661ef2b5d75ba22c7c930025bd95e338f2a6e15bc
|
||||||
|
# via -r requirements.in
|
||||||
|
unpaddedbase32==0.2.0 \
|
||||||
|
--hash=sha256:4aacee75f8fd6c8cf129842ecba45ca59c11bfb13dae19d86f32b48fa3715403 \
|
||||||
|
--hash=sha256:b7b780c31d27d55e66abf6c221216a35690ee8892c2daacff7f2528e229bd9c3
|
||||||
|
# via -r requirements.in
|
||||||
|
urllib3==1.25.11 \
|
||||||
|
--hash=sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2 \
|
||||||
|
--hash=sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# requests
|
||||||
|
watchdog==1.0.2 \
|
||||||
|
--hash=sha256:016b01495b9c55b5d4126ed8ae75d93ea0d99377084107c33162df52887cee18 \
|
||||||
|
--hash=sha256:101532b8db506559e52a9b5d75a308729b3f68264d930670e6155c976d0e52a0 \
|
||||||
|
--hash=sha256:27d9b4666938d5d40afdcdf2c751781e9ce36320788b70208d0f87f7401caf93 \
|
||||||
|
--hash=sha256:2f1ade0d0802503fda4340374d333408831cff23da66d7e711e279ba50fe6c4a \
|
||||||
|
--hash=sha256:376cbc2a35c0392b0fe7ff16fbc1b303fd99d4dd9911ab5581ee9d69adc88982 \
|
||||||
|
--hash=sha256:57f05e55aa603c3b053eed7e679f0a83873c540255b88d58c6223c7493833bac \
|
||||||
|
--hash=sha256:5f1f3b65142175366ba94c64d8d4c8f4015825e0beaacee1c301823266b47b9b \
|
||||||
|
--hash=sha256:602dbd9498592eacc42e0632c19781c3df1728ef9cbab555fab6778effc29eeb \
|
||||||
|
--hash=sha256:68744de2003a5ea2dfbb104f9a74192cf381334a9e2c0ed2bbe1581828d50b61 \
|
||||||
|
--hash=sha256:85e6574395aa6c1e14e0f030d9d7f35c2340a6cf95d5671354ce876ac3ffdd4d \
|
||||||
|
--hash=sha256:b1d723852ce90a14abf0ec0ca9e80689d9509ee4c9ee27163118d87b564a12ac \
|
||||||
|
--hash=sha256:d948ad9ab9aba705f9836625b32e965b9ae607284811cd98334423f659ea537a \
|
||||||
|
--hash=sha256:e2a531e71be7b5cc3499ae2d1494d51b6a26684bcc7c3146f63c810c00e8a3cc \
|
||||||
|
--hash=sha256:e7c73edef48f4ceeebb987317a67e0080e5c9228601ff67b3c4062fa020403c7 \
|
||||||
|
--hash=sha256:ee21aeebe6b3e51e4ba64564c94cee8dbe7438b9cb60f0bb350c4fa70d1b52c2 \
|
||||||
|
--hash=sha256:f1d0e878fd69129d0d68b87cee5d9543f20d8018e82998efb79f7e412d42154a \
|
||||||
|
--hash=sha256:f84146f7864339c8addf2c2b9903271df21d18d2c721e9a77f779493234a82b5
|
||||||
|
# via -r requirements.in
|
||||||
|
werkzeug==1.0.1 \
|
||||||
|
--hash=sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43 \
|
||||||
|
--hash=sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c
|
||||||
|
# via flask
|
||||||
|
zope.event==4.5.0 \
|
||||||
|
--hash=sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42 \
|
||||||
|
--hash=sha256:5e76517f5b9b119acf37ca8819781db6c16ea433f7e2062c4afc2b6fbedb1330
|
||||||
|
# via gevent
|
||||||
|
zope.interface==5.2.0 \
|
||||||
|
--hash=sha256:05a97ba92c1c7c26f25c9f671aa1ef85ffead6cdad13770e5b689cf983adc7e1 \
|
||||||
|
--hash=sha256:07d61722dd7d85547b7c6b0f5486b4338001fab349f2ac5cabc0b7182eb3425d \
|
||||||
|
--hash=sha256:0a990dcc97806e5980bbb54b2e46b9cde9e48932d8e6984daf71ef1745516123 \
|
||||||
|
--hash=sha256:150e8bcb7253a34a4535aeea3de36c0bb3b1a6a47a183a95d65a194b3e07f232 \
|
||||||
|
--hash=sha256:1743bcfe45af8846b775086471c28258f4c6e9ee8ef37484de4495f15a98b549 \
|
||||||
|
--hash=sha256:1b5f6c8fff4ed32aa2dd43e84061bc8346f32d3ba6ad6e58f088fe109608f102 \
|
||||||
|
--hash=sha256:21e49123f375703cf824214939d39df0af62c47d122d955b2a8d9153ea08cfd5 \
|
||||||
|
--hash=sha256:21f579134a47083ffb5ddd1307f0405c91aa8b61ad4be6fd5af0171474fe0c45 \
|
||||||
|
--hash=sha256:27c267dc38a0f0079e96a2945ee65786d38ef111e413c702fbaaacbab6361d00 \
|
||||||
|
--hash=sha256:299bde0ab9e5c4a92f01a152b7fbabb460f31343f1416f9b7b983167ab1e33bc \
|
||||||
|
--hash=sha256:2ab88d8f228f803fcb8cb7d222c579d13dab2d3622c51e8cf321280da01102a7 \
|
||||||
|
--hash=sha256:2ced4c35061eea623bc84c7711eedce8ecc3c2c51cd9c6afa6290df3bae9e104 \
|
||||||
|
--hash=sha256:2dcab01c660983ba5e5a612e0c935141ccbee67d2e2e14b833e01c2354bd8034 \
|
||||||
|
--hash=sha256:32546af61a9a9b141ca38d971aa6eb9800450fa6620ce6323cc30eec447861f3 \
|
||||||
|
--hash=sha256:32b40a4c46d199827d79c86bb8cb88b1bbb764f127876f2cb6f3a47f63dbada3 \
|
||||||
|
--hash=sha256:3cc94c69f6bd48ed86e8e24f358cb75095c8129827df1298518ab860115269a4 \
|
||||||
|
--hash=sha256:42b278ac0989d6f5cf58d7e0828ea6b5951464e3cf2ff229dd09a96cb6ba0c86 \
|
||||||
|
--hash=sha256:495b63fd0302f282ee6c1e6ea0f1c12cb3d1a49c8292d27287f01845ff252a96 \
|
||||||
|
--hash=sha256:4af87cdc0d4b14e600e6d3d09793dce3b7171348a094ba818e2a68ae7ee67546 \
|
||||||
|
--hash=sha256:4b94df9f2fdde7b9314321bab8448e6ad5a23b80542dcab53e329527d4099dcb \
|
||||||
|
--hash=sha256:4c48ddb63e2b20fba4c6a2bf81b4d49e99b6d4587fb67a6cd33a2c1f003af3e3 \
|
||||||
|
--hash=sha256:4df9afd17bd5477e9f8c8b6bb8507e18dd0f8b4efe73bb99729ff203279e9e3b \
|
||||||
|
--hash=sha256:518950fe6a5d56f94ba125107895f938a4f34f704c658986eae8255edb41163b \
|
||||||
|
--hash=sha256:538298e4e113ccb8b41658d5a4b605bebe75e46a30ceca22a5a289cf02c80bec \
|
||||||
|
--hash=sha256:55465121e72e208a7b69b53de791402affe6165083b2ea71b892728bd19ba9ae \
|
||||||
|
--hash=sha256:588384d70a0f19b47409cfdb10e0c27c20e4293b74fc891df3d8eb47782b8b3e \
|
||||||
|
--hash=sha256:6278c080d4afffc9016e14325f8734456831124e8c12caa754fd544435c08386 \
|
||||||
|
--hash=sha256:64ea6c221aeee4796860405e1aedec63424cda4202a7ad27a5066876db5b0fd2 \
|
||||||
|
--hash=sha256:681dbb33e2b40262b33fd383bae63c36d33fd79fa1a8e4092945430744ffd34a \
|
||||||
|
--hash=sha256:6936aa9da390402d646a32a6a38d5409c2d2afb2950f045a7d02ab25a4e7d08d \
|
||||||
|
--hash=sha256:778d0ec38bbd288b150a3ae363c8ffd88d2207a756842495e9bffd8a8afbc89a \
|
||||||
|
--hash=sha256:8251f06a77985a2729a8bdbefbae79ee78567dddc3acbd499b87e705ca59fe24 \
|
||||||
|
--hash=sha256:83b4aa5344cce005a9cff5d0321b2e318e871cc1dfc793b66c32dd4f59e9770d \
|
||||||
|
--hash=sha256:844fad925ac5c2ad4faaceb3b2520ad016b5280105c6e16e79838cf951903a7b \
|
||||||
|
--hash=sha256:8ceb3667dd13b8133f2e4d637b5b00f240f066448e2aa89a41f4c2d78a26ce50 \
|
||||||
|
--hash=sha256:92dc0fb79675882d0b6138be4bf0cec7ea7c7eede60aaca78303d8e8dbdaa523 \
|
||||||
|
--hash=sha256:9789bd945e9f5bd026ed3f5b453d640befb8b1fc33a779c1fe8d3eb21fe3fb4a \
|
||||||
|
--hash=sha256:a2b6d6eb693bc2fc6c484f2e5d93bd0b0da803fa77bf974f160533e555e4d095 \
|
||||||
|
--hash=sha256:aab9f1e34d810feb00bf841993552b8fcc6ae71d473c505381627143d0018a6a \
|
||||||
|
--hash=sha256:abb61afd84f23099ac6099d804cdba9bd3b902aaaded3ffff47e490b0a495520 \
|
||||||
|
--hash=sha256:adf9ee115ae8ff8b6da4b854b4152f253b390ba64407a22d75456fe07dcbda65 \
|
||||||
|
--hash=sha256:aedc6c672b351afe6dfe17ff83ee5e7eb6ed44718f879a9328a68bdb20b57e11 \
|
||||||
|
--hash=sha256:b7a00ecb1434f8183395fac5366a21ee73d14900082ca37cf74993cf46baa56c \
|
||||||
|
--hash=sha256:ba32f4a91c1cb7314c429b03afbf87b1fff4fb1c8db32260e7310104bd77f0c7 \
|
||||||
|
--hash=sha256:cbd0f2cbd8689861209cd89141371d3a22a11613304d1f0736492590aa0ab332 \
|
||||||
|
--hash=sha256:e4bc372b953bf6cec65a8d48482ba574f6e051621d157cf224227dbb55486b1e \
|
||||||
|
--hash=sha256:eccac3d9aadc68e994b6d228cb0c8919fc47a5350d85a1b4d3d81d1e98baf40c \
|
||||||
|
--hash=sha256:efd550b3da28195746bb43bd1d815058181a7ca6d9d6aa89dd37f5eefe2cacb7 \
|
||||||
|
--hash=sha256:efef581c8ba4d990770875e1a2218e856849d32ada2680e53aebc5d154a17e20 \
|
||||||
|
--hash=sha256:f057897711a630a0b7a6a03f1acf379b6ba25d37dc5dc217a97191984ba7f2fc \
|
||||||
|
--hash=sha256:f37d45fab14ffef9d33a0dc3bc59ce0c5313e2253323312d47739192da94f5fd \
|
||||||
|
--hash=sha256:f44906f70205d456d503105023041f1e63aece7623b31c390a0103db4de17537
|
||||||
|
# via gevent
|
||||||
|
|
||||||
|
# WARNING: The following packages were not pinned, but pip requires them to be
|
||||||
|
# pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag.
|
||||||
|
# setuptools
|
||||||
|
|
@ -1,270 +0,0 @@
|
|||||||
#
|
|
||||||
# This file is autogenerated by pip-compile with python 3.10
|
|
||||||
# To update, run:
|
|
||||||
#
|
|
||||||
# pip-compile --generate-hashes --output-file=requirements-base-x86.txt requirements-base.in
|
|
||||||
#
|
|
||||||
cffi==1.15.1 \
|
|
||||||
--hash=sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5 \
|
|
||||||
--hash=sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef \
|
|
||||||
--hash=sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104 \
|
|
||||||
--hash=sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426 \
|
|
||||||
--hash=sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405 \
|
|
||||||
--hash=sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375 \
|
|
||||||
--hash=sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a \
|
|
||||||
--hash=sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e \
|
|
||||||
--hash=sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc \
|
|
||||||
--hash=sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf \
|
|
||||||
--hash=sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185 \
|
|
||||||
--hash=sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497 \
|
|
||||||
--hash=sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3 \
|
|
||||||
--hash=sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35 \
|
|
||||||
--hash=sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c \
|
|
||||||
--hash=sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83 \
|
|
||||||
--hash=sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21 \
|
|
||||||
--hash=sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca \
|
|
||||||
--hash=sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984 \
|
|
||||||
--hash=sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac \
|
|
||||||
--hash=sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd \
|
|
||||||
--hash=sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee \
|
|
||||||
--hash=sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a \
|
|
||||||
--hash=sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2 \
|
|
||||||
--hash=sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192 \
|
|
||||||
--hash=sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7 \
|
|
||||||
--hash=sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585 \
|
|
||||||
--hash=sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f \
|
|
||||||
--hash=sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e \
|
|
||||||
--hash=sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27 \
|
|
||||||
--hash=sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b \
|
|
||||||
--hash=sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e \
|
|
||||||
--hash=sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e \
|
|
||||||
--hash=sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d \
|
|
||||||
--hash=sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c \
|
|
||||||
--hash=sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415 \
|
|
||||||
--hash=sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82 \
|
|
||||||
--hash=sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02 \
|
|
||||||
--hash=sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314 \
|
|
||||||
--hash=sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325 \
|
|
||||||
--hash=sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c \
|
|
||||||
--hash=sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3 \
|
|
||||||
--hash=sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914 \
|
|
||||||
--hash=sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045 \
|
|
||||||
--hash=sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d \
|
|
||||||
--hash=sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9 \
|
|
||||||
--hash=sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5 \
|
|
||||||
--hash=sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2 \
|
|
||||||
--hash=sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c \
|
|
||||||
--hash=sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3 \
|
|
||||||
--hash=sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2 \
|
|
||||||
--hash=sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8 \
|
|
||||||
--hash=sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d \
|
|
||||||
--hash=sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d \
|
|
||||||
--hash=sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9 \
|
|
||||||
--hash=sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162 \
|
|
||||||
--hash=sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76 \
|
|
||||||
--hash=sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4 \
|
|
||||||
--hash=sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e \
|
|
||||||
--hash=sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9 \
|
|
||||||
--hash=sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6 \
|
|
||||||
--hash=sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b \
|
|
||||||
--hash=sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01 \
|
|
||||||
--hash=sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0
|
|
||||||
# via pynacl
|
|
||||||
filenuke==0.0.0 \
|
|
||||||
--hash=sha256:147011c0125121469cae0a8a7f4df399f470e54aa29a08f2d2c099bf0118dcee \
|
|
||||||
--hash=sha256:c55535dcecfdb27c5f4ce664d46e115950b5429763b5db75c198053646177f8f
|
|
||||||
# via -r requirements-base.in
|
|
||||||
kasten==3.0.0 \
|
|
||||||
--hash=sha256:52894af46d6e1339f0d5fa8961892b292f99176848bce11877fe4a435b6782e5 \
|
|
||||||
--hash=sha256:b22ebdc5f475c2ef9ab74abc36552add0b37732a7ce2be6bd7977ee41b2163b4
|
|
||||||
# via onionrblocks
|
|
||||||
mimcvdf==1.2.1 \
|
|
||||||
--hash=sha256:7c837c46cfb9dce4ba895bc706a69646d4d5185c66aeaa333b5cfaa9a7d06dc4
|
|
||||||
# via kasten
|
|
||||||
msgpack==1.0.4 \
|
|
||||||
--hash=sha256:002b5c72b6cd9b4bafd790f364b8480e859b4712e91f43014fe01e4f957b8467 \
|
|
||||||
--hash=sha256:0a68d3ac0104e2d3510de90a1091720157c319ceeb90d74f7b5295a6bee51bae \
|
|
||||||
--hash=sha256:0df96d6eaf45ceca04b3f3b4b111b86b33785683d682c655063ef8057d61fd92 \
|
|
||||||
--hash=sha256:0dfe3947db5fb9ce52aaea6ca28112a170db9eae75adf9339a1aec434dc954ef \
|
|
||||||
--hash=sha256:0e3590f9fb9f7fbc36df366267870e77269c03172d086fa76bb4eba8b2b46624 \
|
|
||||||
--hash=sha256:11184bc7e56fd74c00ead4f9cc9a3091d62ecb96e97653add7a879a14b003227 \
|
|
||||||
--hash=sha256:112b0f93202d7c0fef0b7810d465fde23c746a2d482e1e2de2aafd2ce1492c88 \
|
|
||||||
--hash=sha256:1276e8f34e139aeff1c77a3cefb295598b504ac5314d32c8c3d54d24fadb94c9 \
|
|
||||||
--hash=sha256:1576bd97527a93c44fa856770197dec00d223b0b9f36ef03f65bac60197cedf8 \
|
|
||||||
--hash=sha256:1e91d641d2bfe91ba4c52039adc5bccf27c335356055825c7f88742c8bb900dd \
|
|
||||||
--hash=sha256:26b8feaca40a90cbe031b03d82b2898bf560027160d3eae1423f4a67654ec5d6 \
|
|
||||||
--hash=sha256:2999623886c5c02deefe156e8f869c3b0aaeba14bfc50aa2486a0415178fce55 \
|
|
||||||
--hash=sha256:2a2df1b55a78eb5f5b7d2a4bb221cd8363913830145fad05374a80bf0877cb1e \
|
|
||||||
--hash=sha256:2bb8cdf50dd623392fa75525cce44a65a12a00c98e1e37bf0fb08ddce2ff60d2 \
|
|
||||||
--hash=sha256:2cc5ca2712ac0003bcb625c96368fd08a0f86bbc1a5578802512d87bc592fe44 \
|
|
||||||
--hash=sha256:35bc0faa494b0f1d851fd29129b2575b2e26d41d177caacd4206d81502d4c6a6 \
|
|
||||||
--hash=sha256:3c11a48cf5e59026ad7cb0dc29e29a01b5a66a3e333dc11c04f7e991fc5510a9 \
|
|
||||||
--hash=sha256:449e57cc1ff18d3b444eb554e44613cffcccb32805d16726a5494038c3b93dab \
|
|
||||||
--hash=sha256:462497af5fd4e0edbb1559c352ad84f6c577ffbbb708566a0abaaa84acd9f3ae \
|
|
||||||
--hash=sha256:4733359808c56d5d7756628736061c432ded018e7a1dff2d35a02439043321aa \
|
|
||||||
--hash=sha256:48f5d88c99f64c456413d74a975bd605a9b0526293218a3b77220a2c15458ba9 \
|
|
||||||
--hash=sha256:49565b0e3d7896d9ea71d9095df15b7f75a035c49be733051c34762ca95bbf7e \
|
|
||||||
--hash=sha256:4ab251d229d10498e9a2f3b1e68ef64cb393394ec477e3370c457f9430ce9250 \
|
|
||||||
--hash=sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce \
|
|
||||||
--hash=sha256:4dea20515f660aa6b7e964433b1808d098dcfcabbebeaaad240d11f909298075 \
|
|
||||||
--hash=sha256:545e3cf0cf74f3e48b470f68ed19551ae6f9722814ea969305794645da091236 \
|
|
||||||
--hash=sha256:63e29d6e8c9ca22b21846234913c3466b7e4ee6e422f205a2988083de3b08cae \
|
|
||||||
--hash=sha256:6916c78f33602ecf0509cc40379271ba0f9ab572b066bd4bdafd7434dee4bc6e \
|
|
||||||
--hash=sha256:6a4192b1ab40f8dca3f2877b70e63799d95c62c068c84dc028b40a6cb03ccd0f \
|
|
||||||
--hash=sha256:6c9566f2c39ccced0a38d37c26cc3570983b97833c365a6044edef3574a00c08 \
|
|
||||||
--hash=sha256:76ee788122de3a68a02ed6f3a16bbcd97bc7c2e39bd4d94be2f1821e7c4a64e6 \
|
|
||||||
--hash=sha256:7760f85956c415578c17edb39eed99f9181a48375b0d4a94076d84148cf67b2d \
|
|
||||||
--hash=sha256:77ccd2af37f3db0ea59fb280fa2165bf1b096510ba9fe0cc2bf8fa92a22fdb43 \
|
|
||||||
--hash=sha256:81fc7ba725464651190b196f3cd848e8553d4d510114a954681fd0b9c479d7e1 \
|
|
||||||
--hash=sha256:85f279d88d8e833ec015650fd15ae5eddce0791e1e8a59165318f371158efec6 \
|
|
||||||
--hash=sha256:9667bdfdf523c40d2511f0e98a6c9d3603be6b371ae9a238b7ef2dc4e7a427b0 \
|
|
||||||
--hash=sha256:a75dfb03f8b06f4ab093dafe3ddcc2d633259e6c3f74bb1b01996f5d8aa5868c \
|
|
||||||
--hash=sha256:ac5bd7901487c4a1dd51a8c58f2632b15d838d07ceedaa5e4c080f7190925bff \
|
|
||||||
--hash=sha256:aca0f1644d6b5a73eb3e74d4d64d5d8c6c3d577e753a04c9e9c87d07692c58db \
|
|
||||||
--hash=sha256:b17be2478b622939e39b816e0aa8242611cc8d3583d1cd8ec31b249f04623243 \
|
|
||||||
--hash=sha256:c1683841cd4fa45ac427c18854c3ec3cd9b681694caf5bff04edb9387602d661 \
|
|
||||||
--hash=sha256:c23080fdeec4716aede32b4e0ef7e213c7b1093eede9ee010949f2a418ced6ba \
|
|
||||||
--hash=sha256:d5b5b962221fa2c5d3a7f8133f9abffc114fe218eb4365e40f17732ade576c8e \
|
|
||||||
--hash=sha256:d603de2b8d2ea3f3bcb2efe286849aa7a81531abc52d8454da12f46235092bcb \
|
|
||||||
--hash=sha256:e83f80a7fec1a62cf4e6c9a660e39c7f878f603737a0cdac8c13131d11d97f52 \
|
|
||||||
--hash=sha256:eb514ad14edf07a1dbe63761fd30f89ae79b42625731e1ccf5e1f1092950eaa6 \
|
|
||||||
--hash=sha256:eba96145051ccec0ec86611fe9cf693ce55f2a3ce89c06ed307de0e085730ec1 \
|
|
||||||
--hash=sha256:ed6f7b854a823ea44cf94919ba3f727e230da29feb4a99711433f25800cf747f \
|
|
||||||
--hash=sha256:f0029245c51fd9473dc1aede1160b0a29f4a912e6b1dd353fa6d317085b219da \
|
|
||||||
--hash=sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f \
|
|
||||||
--hash=sha256:fb62ea4b62bfcb0b380d5680f9a4b3f9a2d166d9394e9bbd9666c0ee09a3645c \
|
|
||||||
--hash=sha256:fcb8a47f43acc113e24e910399376f7277cf8508b27e5b88499f053de6b115a8
|
|
||||||
# via kasten
|
|
||||||
onionrblocks==7.0.0 \
|
|
||||||
--hash=sha256:53e90964371076d9daf2ed0790b21f174ef3321f4f1808209cc6dd9b7ff6d8ff \
|
|
||||||
--hash=sha256:54af28d0be856209525646c4ef9f977f95f0ae1329b2cc023b351317c9d0eef7
|
|
||||||
# via -r requirements-base.in
|
|
||||||
ordered-set==4.1.0 \
|
|
||||||
--hash=sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562 \
|
|
||||||
--hash=sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8
|
|
||||||
# via -r requirements-base.in
|
|
||||||
psutil==5.9.3 \
|
|
||||||
--hash=sha256:07d880053c6461c9b89cd5d4808f3b8336665fa3acdefd6777662c5ed73a851a \
|
|
||||||
--hash=sha256:12500d761ac091f2426567f19f95fd3f15a197d96befb44a5c1e3cbe6db5752c \
|
|
||||||
--hash=sha256:1b540599481c73408f6b392cdffef5b01e8ff7a2ac8caae0a91b8222e88e8f1e \
|
|
||||||
--hash=sha256:35feafe232d1aaf35d51bd42790cbccb882456f9f18cdc411532902370d660df \
|
|
||||||
--hash=sha256:3a7826e68b0cf4ce2c1ee385d64eab7d70e3133171376cac53d7c1790357ec8f \
|
|
||||||
--hash=sha256:46907fa62acaac364fff0b8a9da7b360265d217e4fdeaca0a2397a6883dffba2 \
|
|
||||||
--hash=sha256:4bd4854f0c83aa84a5a40d3b5d0eb1f3c128f4146371e03baed4589fe4f3c931 \
|
|
||||||
--hash=sha256:538fcf6ae856b5e12d13d7da25ad67f02113c96f5989e6ad44422cb5994ca7fc \
|
|
||||||
--hash=sha256:547ebb02031fdada635452250ff39942db8310b5c4a8102dfe9384ee5791e650 \
|
|
||||||
--hash=sha256:5e8b50241dd3c2ed498507f87a6602825073c07f3b7e9560c58411c14fe1e1c9 \
|
|
||||||
--hash=sha256:5fa88e3d5d0b480602553d362c4b33a63e0c40bfea7312a7bf78799e01e0810b \
|
|
||||||
--hash=sha256:68fa227c32240c52982cb931801c5707a7f96dd8927f9102d6c7771ea1ff5698 \
|
|
||||||
--hash=sha256:6ced1ad823ecfa7d3ce26fe8aa4996e2e53fb49b7fed8ad81c80958501ec0619 \
|
|
||||||
--hash=sha256:71b1206e7909792d16933a0d2c1c7f04ae196186c51ba8567abae1d041f06dcb \
|
|
||||||
--hash=sha256:767ef4fa33acda16703725c0473a91e1832d296c37c63896c7153ba81698f1ab \
|
|
||||||
--hash=sha256:7ccfcdfea4fc4b0a02ca2c31de7fcd186beb9cff8207800e14ab66f79c773af6 \
|
|
||||||
--hash=sha256:7e4939ff75149b67aef77980409f156f0082fa36accc475d45c705bb00c6c16a \
|
|
||||||
--hash=sha256:828c9dc9478b34ab96be75c81942d8df0c2bb49edbb481f597314d92b6441d89 \
|
|
||||||
--hash=sha256:8a4e07611997acf178ad13b842377e3d8e9d0a5bac43ece9bfc22a96735d9a4f \
|
|
||||||
--hash=sha256:941a6c2c591da455d760121b44097781bc970be40e0e43081b9139da485ad5b7 \
|
|
||||||
--hash=sha256:9a4af6ed1094f867834f5f07acd1250605a0874169a5fcadbcec864aec2496a6 \
|
|
||||||
--hash=sha256:9ec296f565191f89c48f33d9544d8d82b0d2af7dd7d2d4e6319f27a818f8d1cc \
|
|
||||||
--hash=sha256:9ec95df684583b5596c82bb380c53a603bb051cf019d5c849c47e117c5064395 \
|
|
||||||
--hash=sha256:a04a1836894c8279e5e0a0127c0db8e198ca133d28be8a2a72b4db16f6cf99c1 \
|
|
||||||
--hash=sha256:a3d81165b8474087bb90ec4f333a638ccfd1d69d34a9b4a1a7eaac06648f9fbe \
|
|
||||||
--hash=sha256:b4a247cd3feaae39bb6085fcebf35b3b8ecd9b022db796d89c8f05067ca28e71 \
|
|
||||||
--hash=sha256:ba38cf9984d5462b506e239cf4bc24e84ead4b1d71a3be35e66dad0d13ded7c1 \
|
|
||||||
--hash=sha256:beb57d8a1ca0ae0eb3d08ccaceb77e1a6d93606f0e1754f0d60a6ebd5c288837 \
|
|
||||||
--hash=sha256:d266cd05bd4a95ca1c2b9b5aac50d249cf7c94a542f47e0b22928ddf8b80d1ef \
|
|
||||||
--hash=sha256:d8c3cc6bb76492133474e130a12351a325336c01c96a24aae731abf5a47fe088 \
|
|
||||||
--hash=sha256:db8e62016add2235cc87fb7ea000ede9e4ca0aa1f221b40cef049d02d5d2593d \
|
|
||||||
--hash=sha256:e7507f6c7b0262d3e7b0eeda15045bf5881f4ada70473b87bc7b7c93b992a7d7 \
|
|
||||||
--hash=sha256:ed15edb14f52925869250b1375f0ff58ca5c4fa8adefe4883cfb0737d32f5c02 \
|
|
||||||
--hash=sha256:f57d63a2b5beaf797b87024d018772439f9d3103a395627b77d17a8d72009543 \
|
|
||||||
--hash=sha256:fa5e32c7d9b60b2528108ade2929b115167fe98d59f89555574715054f50fa31 \
|
|
||||||
--hash=sha256:fe79b4ad4836e3da6c4650cb85a663b3a51aef22e1a829c384e18fae87e5e727
|
|
||||||
# via -r requirements-base.in
|
|
||||||
pycparser==2.21 \
|
|
||||||
--hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \
|
|
||||||
--hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206
|
|
||||||
# via cffi
|
|
||||||
pynacl==1.5.0 \
|
|
||||||
--hash=sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858 \
|
|
||||||
--hash=sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d \
|
|
||||||
--hash=sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93 \
|
|
||||||
--hash=sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1 \
|
|
||||||
--hash=sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92 \
|
|
||||||
--hash=sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff \
|
|
||||||
--hash=sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba \
|
|
||||||
--hash=sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394 \
|
|
||||||
--hash=sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b \
|
|
||||||
--hash=sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543
|
|
||||||
# via onionrblocks
|
|
||||||
result==0.8.0 \
|
|
||||||
--hash=sha256:c48c909e92181a075ba358228a3fe161e26d205dad416ad81f27f23515a5626d \
|
|
||||||
--hash=sha256:d6a6258f32c057a4e0478999c6ce43dcadaf8ea435f58ac601ae2768f93ef243
|
|
||||||
# via -r requirements-base.in
|
|
||||||
ujson==5.5.0 \
|
|
||||||
--hash=sha256:0762a4fdf86e01f3f8d8b6b7158d01fdd870799ff3f402b676e358fcd879e7eb \
|
|
||||||
--hash=sha256:10095160dbe6bba8059ad6677a01da251431f4c68041bf796dcac0956b34f8f7 \
|
|
||||||
--hash=sha256:1a485117f97312bef45f5d79d2ff97eff4da503b8a04f3691f59d31141686459 \
|
|
||||||
--hash=sha256:1cef44ea4973344baed3d50a5da4a8843de3a6af7dea7fadf0a594e53ce5892f \
|
|
||||||
--hash=sha256:1dc2f46c31ef22b0aaa28cd71be897bea271e700636658d573df9c43c49ebbd0 \
|
|
||||||
--hash=sha256:21678d7e068707e4d54bdfeb8c250ebc548b51e499aed778b22112ca31a79669 \
|
|
||||||
--hash=sha256:278aa9d7cb56435c96d19f5d702e026bcf69f824e24b41e9b52706abd3565837 \
|
|
||||||
--hash=sha256:2ab011e3556a9a1d9461bd686870c527327765ed02fe53550531d6609a8a33ff \
|
|
||||||
--hash=sha256:2d90414e3b4b44b39825049185959488e084ea7fcaf6124afd5c00893938b09d \
|
|
||||||
--hash=sha256:2e506ecf89b6b9d304362ccef770831ec242a52c89dab1b4aabf1ab0eb1d5ed6 \
|
|
||||||
--hash=sha256:33cd9084fefc74cbacf88c92fd260b61211e00bcde38d640c369e5dc34a2b4e1 \
|
|
||||||
--hash=sha256:3b74467564814fbce322427a5664e6bcc7dae6dbc8acbef76300fe43ca4072ab \
|
|
||||||
--hash=sha256:3f3f4240d99d55eb97cb012e9adf401f5ed9cd827af0341ac44603832202b0d2 \
|
|
||||||
--hash=sha256:3fe1aea596f9539fc20cd9e52f098c842afc090168824fd4ca9744fe13151a03 \
|
|
||||||
--hash=sha256:4a8cb3c8637006c5bd8237ebb5992a76ba06e39988ad5cff2096227443e8fd6a \
|
|
||||||
--hash=sha256:4ef4ab8352861b99bd7fedb1fc6df3ea7f7d5216c789ba6d859e4ea06f1a4c45 \
|
|
||||||
--hash=sha256:5035bb997d163f346c22abcec75190e7e756a5349e7c708bd3d5fd7066a9a854 \
|
|
||||||
--hash=sha256:593a0f6fb0e186c5ba65465ed6f6215a30d1efa898c25e74de1c8577a1bff6d0 \
|
|
||||||
--hash=sha256:59cdcd934385f36e8bd76aedc234371cc75c848d95bdce804ac8aa8744cfeffa \
|
|
||||||
--hash=sha256:5a9b1320d8363a42d857fae8065a2174d38217cdd58cd8dc4f48d54e0591271e \
|
|
||||||
--hash=sha256:5f9681ec4c60d0da590552427d770636d9079038c30b265f507ccde23caa7823 \
|
|
||||||
--hash=sha256:5fd797a4837ba10671954e7c09010cec7aca67e09d193f4920a16beea5f66f65 \
|
|
||||||
--hash=sha256:6019e3480d933d3698f2ecb4b46d64bfadd64e718f04fac36e681f3254b49a93 \
|
|
||||||
--hash=sha256:603607f56a0ee84d9cd2c7e9b1d29b18a70684b94ee34f07b9ffe8dc9c8a9f81 \
|
|
||||||
--hash=sha256:60a4b481978ea2aad8fe8af1ecc271624d01b3cf4b09e9b643dd2fe19c07634c \
|
|
||||||
--hash=sha256:6b9812638d7aa8ecda2e8e1513fb4da999249603bffab7439a5f8f0bb362b0db \
|
|
||||||
--hash=sha256:6c7ae6e0778ab9610f5e80e0595957d101ab8de18c32a8c053a19943ef4831d0 \
|
|
||||||
--hash=sha256:6f83be8257b2f2dd6dea5ee62cd28db90584da7a7af1fba77a2102fc7943638a \
|
|
||||||
--hash=sha256:701e81e047f5c0cffd4ac828efca68b0bd270c616654966a051e9a5f836b385e \
|
|
||||||
--hash=sha256:703fd69d9cb21d6ec2086789df9be2cf8140a76ff127050c24007ea8940dcd3b \
|
|
||||||
--hash=sha256:7471d4486f23518cff343f1eec6c68d1b977ed74c3e6cc3e1ac896b9b7d68645 \
|
|
||||||
--hash=sha256:765d46f3d5e7a1d48075035e2d1a9164f683e3fccde834ca04602e6c588835bc \
|
|
||||||
--hash=sha256:7a09d203983104918c62f2eef9406f24c355511f9217967df23e70fa7f5b54ff \
|
|
||||||
--hash=sha256:7c20cc83b0df47129ec6ed8a47fa7dcfc309c5bad029464004162738502568bb \
|
|
||||||
--hash=sha256:7d7cfac2547c93389fa303fc0c0eb6698825564e8389c41c9b60009c746207b6 \
|
|
||||||
--hash=sha256:7d87c817b292efb748f1974f37e8bb8a8772ef92f05f84e507159360814bcc3f \
|
|
||||||
--hash=sha256:8141f654432cf75144d6103bfac2286b8adf23467201590b173a74535d6be22d \
|
|
||||||
--hash=sha256:849f2ff40264152f25589cb48ddb4a43d14db811f841ec73989bfc0c8c4853fa \
|
|
||||||
--hash=sha256:880c84ce59f49776cf120f77e7ca04877c97c6887917078dbc369eb47004d7cf \
|
|
||||||
--hash=sha256:94874584b733a18b310b0e954d53168e62cd4a0fd9db85b1903f0902a7eb33e8 \
|
|
||||||
--hash=sha256:95603eff711b8f3b9596e1c961dbeb745a792ba1904141612f194e07edd71e5f \
|
|
||||||
--hash=sha256:9585892091ae86045135d6a6129a644142d6a51b23e1428bb5de6d10bc0ce0c7 \
|
|
||||||
--hash=sha256:977bf5be704a88d46bf5b228df8b44521b1f3119d741062191608b3a6a38f224 \
|
|
||||||
--hash=sha256:9cdc46859024501c20ab74ad542cdf2f08b94b5ce384f2f569483fa3ed926d04 \
|
|
||||||
--hash=sha256:a34a5f034b339f69ef7f6a134c22d04b92e07b6ddc1dd65382e7e4ec65d6437d \
|
|
||||||
--hash=sha256:a655f7b755cfc5c07f2116b6dcf0ba148c89adef9a6d40c1b0f1fada878c4345 \
|
|
||||||
--hash=sha256:a7d12f2d2df195c8c4e49d2cdbad640353a856c62ca2c624d8b47aa33b65a2a2 \
|
|
||||||
--hash=sha256:abfe83e082c9208891e2158c1b5044a650ecec408b823bf6bf16cd7f8085cafa \
|
|
||||||
--hash=sha256:b25077a971c7da47bd6846a912a747f6963776d90720c88603b1b55d81790780 \
|
|
||||||
--hash=sha256:bf416a93e1331820c77e3429df26946dbd4fe105e9b487cd2d1b7298b75784a8 \
|
|
||||||
--hash=sha256:c04ae27e076d81a3839047d8eed57c1e17e361640616fd520d752375e3ba8f0c \
|
|
||||||
--hash=sha256:d5bea13c73f36c4346808df3fa806596163a7962b6d28001ca2a391cab856089 \
|
|
||||||
--hash=sha256:d75bef34e69e7effb7b4849e3f830e3174d2cc6ec7273503fdde111c222dc9b3 \
|
|
||||||
--hash=sha256:d93940664a5ccfd79f72dcb939b0c31a3479889f14f0eb95ec52976f8c0cae7d \
|
|
||||||
--hash=sha256:d9c89c521dc90c7564358e525f849b93ad1d710553c1491f66b8cce8113bc901 \
|
|
||||||
--hash=sha256:e0b36257dc90194784531c3b922d8d31fb2b4d8e5adfd27aff4eee7174176365 \
|
|
||||||
--hash=sha256:e1135264bcd40965cd35b0869e36952f54825024befdc7a923df9a7d83cfd800 \
|
|
||||||
--hash=sha256:e510d288e613d6927796dfb728e13e4530fc83b9ccac5888a21f7860486eab21 \
|
|
||||||
--hash=sha256:ee9a2c9a4b2421e77f8fe33ed0621dea03c66c710707553020b1e32f3afb6240 \
|
|
||||||
--hash=sha256:f19f11055ba2961eb39bdb1ff15763a53fca4fa0b5b624da3c7a528e83cdd09c \
|
|
||||||
--hash=sha256:f26544bc10c83a2ff9aa2e093500c1b473f327faae31fb468d591e5823333376 \
|
|
||||||
--hash=sha256:f4875cafc9a6482c04c7df52a725d1c41beb74913c0ff4ec8f189f1954a2afe9 \
|
|
||||||
--hash=sha256:f5179088ef6487c475604b7898731a6ddeeada7702cfb2162155b016703a8475 \
|
|
||||||
--hash=sha256:f63d1ae1ca17bb2c847e298c7bcf084a73d56d434b4c50509fb93a4b4300b0b2 \
|
|
||||||
--hash=sha256:ff4928dc1e9704b567171c16787238201fdbf023665573c12c02146fe1e02eec
|
|
||||||
# via -r requirements-base.in
|
|
@ -1,7 +0,0 @@
|
|||||||
psutil==5.9.3
|
|
||||||
ujson==5.5.0
|
|
||||||
ordered-set==4.1.0
|
|
||||||
result==0.8.0
|
|
||||||
# These two are also by Kevin
|
|
||||||
filenuke==0.0.0
|
|
||||||
onionrblocks==7.0.0
|
|
1
requirements-notifications.in
Normal file
1
requirements-notifications.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
simplenotifications==0.2.18
|
8
requirements-notifications.txt
Normal file
8
requirements-notifications.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile
|
||||||
|
# To update, run:
|
||||||
|
#
|
||||||
|
# pip-compile --generate-hashes --output-file=requirements-notifications.txt requirements-notifications.in
|
||||||
|
#
|
||||||
|
simplenotifications==0.2.18 \
|
||||||
|
--hash=sha256:b7efd3d834b1922a3279287913d87fe4ef6fad181ca7edf54bfb8f1d973941e0
|
@ -1,445 +0,0 @@
|
|||||||
#
|
|
||||||
# This file is autogenerated by pip-compile with python 3.10
|
|
||||||
# To update, run:
|
|
||||||
#
|
|
||||||
# pip-compile --generate-hashes --output-file=requirements-x86-all-plugins.txt requirements-base.in static-data/official-plugins/rpc/requirements.in static-data/official-plugins/tor/requirements.in static-data/official-plugins/wot/requirements.in
|
|
||||||
#
|
|
||||||
autocommand==2.2.1 \
|
|
||||||
--hash=sha256:85d03044c2a1fc1c7844ac41545045927aecde0cbaf8ea28b88e0cd8588ce5d3 \
|
|
||||||
--hash=sha256:fed420e9d02745821a782971b583c6970259ee0b229be2a0a401e1467a4f170f
|
|
||||||
# via jaraco-text
|
|
||||||
cffi==1.15.1 \
|
|
||||||
--hash=sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5 \
|
|
||||||
--hash=sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef \
|
|
||||||
--hash=sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104 \
|
|
||||||
--hash=sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426 \
|
|
||||||
--hash=sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405 \
|
|
||||||
--hash=sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375 \
|
|
||||||
--hash=sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a \
|
|
||||||
--hash=sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e \
|
|
||||||
--hash=sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc \
|
|
||||||
--hash=sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf \
|
|
||||||
--hash=sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185 \
|
|
||||||
--hash=sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497 \
|
|
||||||
--hash=sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3 \
|
|
||||||
--hash=sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35 \
|
|
||||||
--hash=sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c \
|
|
||||||
--hash=sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83 \
|
|
||||||
--hash=sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21 \
|
|
||||||
--hash=sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca \
|
|
||||||
--hash=sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984 \
|
|
||||||
--hash=sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac \
|
|
||||||
--hash=sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd \
|
|
||||||
--hash=sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee \
|
|
||||||
--hash=sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a \
|
|
||||||
--hash=sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2 \
|
|
||||||
--hash=sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192 \
|
|
||||||
--hash=sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7 \
|
|
||||||
--hash=sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585 \
|
|
||||||
--hash=sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f \
|
|
||||||
--hash=sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e \
|
|
||||||
--hash=sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27 \
|
|
||||||
--hash=sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b \
|
|
||||||
--hash=sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e \
|
|
||||||
--hash=sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e \
|
|
||||||
--hash=sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d \
|
|
||||||
--hash=sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c \
|
|
||||||
--hash=sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415 \
|
|
||||||
--hash=sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82 \
|
|
||||||
--hash=sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02 \
|
|
||||||
--hash=sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314 \
|
|
||||||
--hash=sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325 \
|
|
||||||
--hash=sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c \
|
|
||||||
--hash=sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3 \
|
|
||||||
--hash=sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914 \
|
|
||||||
--hash=sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045 \
|
|
||||||
--hash=sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d \
|
|
||||||
--hash=sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9 \
|
|
||||||
--hash=sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5 \
|
|
||||||
--hash=sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2 \
|
|
||||||
--hash=sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c \
|
|
||||||
--hash=sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3 \
|
|
||||||
--hash=sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2 \
|
|
||||||
--hash=sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8 \
|
|
||||||
--hash=sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d \
|
|
||||||
--hash=sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d \
|
|
||||||
--hash=sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9 \
|
|
||||||
--hash=sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162 \
|
|
||||||
--hash=sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76 \
|
|
||||||
--hash=sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4 \
|
|
||||||
--hash=sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e \
|
|
||||||
--hash=sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9 \
|
|
||||||
--hash=sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6 \
|
|
||||||
--hash=sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b \
|
|
||||||
--hash=sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01 \
|
|
||||||
--hash=sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0
|
|
||||||
# via
|
|
||||||
# -r static-data/official-plugins/wot/requirements.in
|
|
||||||
# cryptography
|
|
||||||
# pynacl
|
|
||||||
cheroot==8.6.0 \
|
|
||||||
--hash=sha256:366adf6e7cac9555486c2d1be6297993022eff6f8c4655c1443268cca3f08e25 \
|
|
||||||
--hash=sha256:62cbced16f07e8aaf512673987cd6b1fc5ad00073345e9ed6c4e2a5cc2a3a22d
|
|
||||||
# via cherrypy
|
|
||||||
cherrypy==18.8.0 \
|
|
||||||
--hash=sha256:9b48cfba8a2f16d5b6419cc657e6d51db005ba35c5e3824e4728bb03bbc7ef9b \
|
|
||||||
--hash=sha256:b56097025dc78a76a59db551b3a82871c6b3a0107b80b12ff759e4c0b3b947ce
|
|
||||||
# via -r static-data/official-plugins/rpc/requirements.in
|
|
||||||
cryptography==38.0.1 \
|
|
||||||
--hash=sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a \
|
|
||||||
--hash=sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f \
|
|
||||||
--hash=sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0 \
|
|
||||||
--hash=sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407 \
|
|
||||||
--hash=sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7 \
|
|
||||||
--hash=sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6 \
|
|
||||||
--hash=sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153 \
|
|
||||||
--hash=sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750 \
|
|
||||||
--hash=sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad \
|
|
||||||
--hash=sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6 \
|
|
||||||
--hash=sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b \
|
|
||||||
--hash=sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5 \
|
|
||||||
--hash=sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a \
|
|
||||||
--hash=sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d \
|
|
||||||
--hash=sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d \
|
|
||||||
--hash=sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294 \
|
|
||||||
--hash=sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0 \
|
|
||||||
--hash=sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a \
|
|
||||||
--hash=sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac \
|
|
||||||
--hash=sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61 \
|
|
||||||
--hash=sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013 \
|
|
||||||
--hash=sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e \
|
|
||||||
--hash=sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb \
|
|
||||||
--hash=sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9 \
|
|
||||||
--hash=sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd \
|
|
||||||
--hash=sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818
|
|
||||||
# via secretstorage
|
|
||||||
filenuke==0.0.0 \
|
|
||||||
--hash=sha256:147011c0125121469cae0a8a7f4df399f470e54aa29a08f2d2c099bf0118dcee \
|
|
||||||
--hash=sha256:c55535dcecfdb27c5f4ce664d46e115950b5429763b5db75c198053646177f8f
|
|
||||||
# via -r requirements-base.in
|
|
||||||
inflect==6.0.0 \
|
|
||||||
--hash=sha256:0bc1516ec2725e2d8221707a612245093cb6f1cea209cfd8cbd4fc5e96fa6365 \
|
|
||||||
--hash=sha256:e3b85d65a296843268f35f4136283ad7c012a129375db1529d49b4b01ecb400b
|
|
||||||
# via jaraco-text
|
|
||||||
jaraco-classes==3.2.2 \
|
|
||||||
--hash=sha256:6745f113b0b588239ceb49532aa09c3ebb947433ce311ef2f8e3ad64ebb74594 \
|
|
||||||
--hash=sha256:e6ef6fd3fcf4579a7a019d87d1e56a883f4e4c35cfe925f86731abc58804e647
|
|
||||||
# via
|
|
||||||
# jaraco-collections
|
|
||||||
# keyring
|
|
||||||
jaraco-collections==3.5.2 \
|
|
||||||
--hash=sha256:072b93eb35f9e48508485755534e66a34ef1cc84af291fd27f39b44d4c0dd2c3 \
|
|
||||||
--hash=sha256:1ca12fa4b7067dfc8d7f791c1a8660d970a2bf2f80536ba0aa5cbb71fe1261f1
|
|
||||||
# via cherrypy
|
|
||||||
jaraco-context==4.1.2 \
|
|
||||||
--hash=sha256:9327d3e6901923e5a7097aa2df4b9c2bc13f845c7672692e3827ebd1b3d67606 \
|
|
||||||
--hash=sha256:a58e94dd67871639abc091b57d32842449b230777570ef2bcec3dc16b912613e
|
|
||||||
# via jaraco-text
|
|
||||||
jaraco-functools==3.5.1 \
|
|
||||||
--hash=sha256:c8774f73323de42250a659934215da1d899b02c66a6133f1cb79f02a5aff4f38 \
|
|
||||||
--hash=sha256:d0adcf91710a0853efe9f23a78fad586bf67df572f0d6d8e0fa36d289ae1c1d9
|
|
||||||
# via
|
|
||||||
# cheroot
|
|
||||||
# jaraco-text
|
|
||||||
# tempora
|
|
||||||
jaraco-text==3.9.1 \
|
|
||||||
--hash=sha256:3ca615c4135e151d21206075ec4aface8a2fbc3e68437fe709a6541428a635f9 \
|
|
||||||
--hash=sha256:d57cd4448a588020318425e04194e897f96fc23b92b82ff9308a24d5cbf2b3fb
|
|
||||||
# via jaraco-collections
|
|
||||||
jeepney==0.8.0 \
|
|
||||||
--hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \
|
|
||||||
--hash=sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755
|
|
||||||
# via
|
|
||||||
# keyring
|
|
||||||
# secretstorage
|
|
||||||
json-rpc==1.13.0 \
|
|
||||||
--hash=sha256:84b45058e5ba95f49c7b6afcf7e03ab86bee89bf2c01f3ad8dd41fe114fc1f84 \
|
|
||||||
--hash=sha256:def0dbcf5b7084fc31d677f2f5990d988d06497f2f47f13024274cfb2d5d7589
|
|
||||||
# via -r static-data/official-plugins/rpc/requirements.in
|
|
||||||
kasten==3.0.0 \
|
|
||||||
--hash=sha256:52894af46d6e1339f0d5fa8961892b292f99176848bce11877fe4a435b6782e5 \
|
|
||||||
--hash=sha256:b22ebdc5f475c2ef9ab74abc36552add0b37732a7ce2be6bd7977ee41b2163b4
|
|
||||||
# via onionrblocks
|
|
||||||
keyring==23.9.3 \
|
|
||||||
--hash=sha256:69732a15cb1433bdfbc3b980a8a36a04878a6cfd7cb99f497b573f31618001c0 \
|
|
||||||
--hash=sha256:69b01dd83c42f590250fe7a1f503fc229b14de83857314b1933a3ddbf595c4a5
|
|
||||||
# via -r static-data/official-plugins/wot/requirements.in
|
|
||||||
mimcvdf==1.2.1 \
|
|
||||||
--hash=sha256:7c837c46cfb9dce4ba895bc706a69646d4d5185c66aeaa333b5cfaa9a7d06dc4
|
|
||||||
# via kasten
|
|
||||||
more-itertools==8.14.0 \
|
|
||||||
--hash=sha256:1bc4f91ee5b1b31ac7ceacc17c09befe6a40a503907baf9c839c229b5095cfd2 \
|
|
||||||
--hash=sha256:c09443cd3d5438b8dafccd867a6bc1cb0894389e90cb53d227456b0b0bccb750
|
|
||||||
# via
|
|
||||||
# cheroot
|
|
||||||
# cherrypy
|
|
||||||
# jaraco-classes
|
|
||||||
# jaraco-functools
|
|
||||||
# jaraco-text
|
|
||||||
msgpack==1.0.4 \
|
|
||||||
--hash=sha256:002b5c72b6cd9b4bafd790f364b8480e859b4712e91f43014fe01e4f957b8467 \
|
|
||||||
--hash=sha256:0a68d3ac0104e2d3510de90a1091720157c319ceeb90d74f7b5295a6bee51bae \
|
|
||||||
--hash=sha256:0df96d6eaf45ceca04b3f3b4b111b86b33785683d682c655063ef8057d61fd92 \
|
|
||||||
--hash=sha256:0dfe3947db5fb9ce52aaea6ca28112a170db9eae75adf9339a1aec434dc954ef \
|
|
||||||
--hash=sha256:0e3590f9fb9f7fbc36df366267870e77269c03172d086fa76bb4eba8b2b46624 \
|
|
||||||
--hash=sha256:11184bc7e56fd74c00ead4f9cc9a3091d62ecb96e97653add7a879a14b003227 \
|
|
||||||
--hash=sha256:112b0f93202d7c0fef0b7810d465fde23c746a2d482e1e2de2aafd2ce1492c88 \
|
|
||||||
--hash=sha256:1276e8f34e139aeff1c77a3cefb295598b504ac5314d32c8c3d54d24fadb94c9 \
|
|
||||||
--hash=sha256:1576bd97527a93c44fa856770197dec00d223b0b9f36ef03f65bac60197cedf8 \
|
|
||||||
--hash=sha256:1e91d641d2bfe91ba4c52039adc5bccf27c335356055825c7f88742c8bb900dd \
|
|
||||||
--hash=sha256:26b8feaca40a90cbe031b03d82b2898bf560027160d3eae1423f4a67654ec5d6 \
|
|
||||||
--hash=sha256:2999623886c5c02deefe156e8f869c3b0aaeba14bfc50aa2486a0415178fce55 \
|
|
||||||
--hash=sha256:2a2df1b55a78eb5f5b7d2a4bb221cd8363913830145fad05374a80bf0877cb1e \
|
|
||||||
--hash=sha256:2bb8cdf50dd623392fa75525cce44a65a12a00c98e1e37bf0fb08ddce2ff60d2 \
|
|
||||||
--hash=sha256:2cc5ca2712ac0003bcb625c96368fd08a0f86bbc1a5578802512d87bc592fe44 \
|
|
||||||
--hash=sha256:35bc0faa494b0f1d851fd29129b2575b2e26d41d177caacd4206d81502d4c6a6 \
|
|
||||||
--hash=sha256:3c11a48cf5e59026ad7cb0dc29e29a01b5a66a3e333dc11c04f7e991fc5510a9 \
|
|
||||||
--hash=sha256:449e57cc1ff18d3b444eb554e44613cffcccb32805d16726a5494038c3b93dab \
|
|
||||||
--hash=sha256:462497af5fd4e0edbb1559c352ad84f6c577ffbbb708566a0abaaa84acd9f3ae \
|
|
||||||
--hash=sha256:4733359808c56d5d7756628736061c432ded018e7a1dff2d35a02439043321aa \
|
|
||||||
--hash=sha256:48f5d88c99f64c456413d74a975bd605a9b0526293218a3b77220a2c15458ba9 \
|
|
||||||
--hash=sha256:49565b0e3d7896d9ea71d9095df15b7f75a035c49be733051c34762ca95bbf7e \
|
|
||||||
--hash=sha256:4ab251d229d10498e9a2f3b1e68ef64cb393394ec477e3370c457f9430ce9250 \
|
|
||||||
--hash=sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce \
|
|
||||||
--hash=sha256:4dea20515f660aa6b7e964433b1808d098dcfcabbebeaaad240d11f909298075 \
|
|
||||||
--hash=sha256:545e3cf0cf74f3e48b470f68ed19551ae6f9722814ea969305794645da091236 \
|
|
||||||
--hash=sha256:63e29d6e8c9ca22b21846234913c3466b7e4ee6e422f205a2988083de3b08cae \
|
|
||||||
--hash=sha256:6916c78f33602ecf0509cc40379271ba0f9ab572b066bd4bdafd7434dee4bc6e \
|
|
||||||
--hash=sha256:6a4192b1ab40f8dca3f2877b70e63799d95c62c068c84dc028b40a6cb03ccd0f \
|
|
||||||
--hash=sha256:6c9566f2c39ccced0a38d37c26cc3570983b97833c365a6044edef3574a00c08 \
|
|
||||||
--hash=sha256:76ee788122de3a68a02ed6f3a16bbcd97bc7c2e39bd4d94be2f1821e7c4a64e6 \
|
|
||||||
--hash=sha256:7760f85956c415578c17edb39eed99f9181a48375b0d4a94076d84148cf67b2d \
|
|
||||||
--hash=sha256:77ccd2af37f3db0ea59fb280fa2165bf1b096510ba9fe0cc2bf8fa92a22fdb43 \
|
|
||||||
--hash=sha256:81fc7ba725464651190b196f3cd848e8553d4d510114a954681fd0b9c479d7e1 \
|
|
||||||
--hash=sha256:85f279d88d8e833ec015650fd15ae5eddce0791e1e8a59165318f371158efec6 \
|
|
||||||
--hash=sha256:9667bdfdf523c40d2511f0e98a6c9d3603be6b371ae9a238b7ef2dc4e7a427b0 \
|
|
||||||
--hash=sha256:a75dfb03f8b06f4ab093dafe3ddcc2d633259e6c3f74bb1b01996f5d8aa5868c \
|
|
||||||
--hash=sha256:ac5bd7901487c4a1dd51a8c58f2632b15d838d07ceedaa5e4c080f7190925bff \
|
|
||||||
--hash=sha256:aca0f1644d6b5a73eb3e74d4d64d5d8c6c3d577e753a04c9e9c87d07692c58db \
|
|
||||||
--hash=sha256:b17be2478b622939e39b816e0aa8242611cc8d3583d1cd8ec31b249f04623243 \
|
|
||||||
--hash=sha256:c1683841cd4fa45ac427c18854c3ec3cd9b681694caf5bff04edb9387602d661 \
|
|
||||||
--hash=sha256:c23080fdeec4716aede32b4e0ef7e213c7b1093eede9ee010949f2a418ced6ba \
|
|
||||||
--hash=sha256:d5b5b962221fa2c5d3a7f8133f9abffc114fe218eb4365e40f17732ade576c8e \
|
|
||||||
--hash=sha256:d603de2b8d2ea3f3bcb2efe286849aa7a81531abc52d8454da12f46235092bcb \
|
|
||||||
--hash=sha256:e83f80a7fec1a62cf4e6c9a660e39c7f878f603737a0cdac8c13131d11d97f52 \
|
|
||||||
--hash=sha256:eb514ad14edf07a1dbe63761fd30f89ae79b42625731e1ccf5e1f1092950eaa6 \
|
|
||||||
--hash=sha256:eba96145051ccec0ec86611fe9cf693ce55f2a3ce89c06ed307de0e085730ec1 \
|
|
||||||
--hash=sha256:ed6f7b854a823ea44cf94919ba3f727e230da29feb4a99711433f25800cf747f \
|
|
||||||
--hash=sha256:f0029245c51fd9473dc1aede1160b0a29f4a912e6b1dd353fa6d317085b219da \
|
|
||||||
--hash=sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f \
|
|
||||||
--hash=sha256:fb62ea4b62bfcb0b380d5680f9a4b3f9a2d166d9394e9bbd9666c0ee09a3645c \
|
|
||||||
--hash=sha256:fcb8a47f43acc113e24e910399376f7277cf8508b27e5b88499f053de6b115a8
|
|
||||||
# via kasten
|
|
||||||
onionrblocks==7.0.0 \
|
|
||||||
--hash=sha256:53e90964371076d9daf2ed0790b21f174ef3321f4f1808209cc6dd9b7ff6d8ff \
|
|
||||||
--hash=sha256:54af28d0be856209525646c4ef9f977f95f0ae1329b2cc023b351317c9d0eef7
|
|
||||||
# via -r requirements-base.in
|
|
||||||
ordered-set==4.1.0 \
|
|
||||||
--hash=sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562 \
|
|
||||||
--hash=sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8
|
|
||||||
# via -r requirements-base.in
|
|
||||||
portend==3.1.0 \
|
|
||||||
--hash=sha256:239e3116045ea823f6df87d6168107ad75ccc0590e37242af0cc1e98c5d224e4 \
|
|
||||||
--hash=sha256:9e735cee3a5c1961f09e3f3ba6dc498198c2d70b473d98d0d1504b8d1e7a3d61
|
|
||||||
# via cherrypy
|
|
||||||
psutil==5.9.3 \
|
|
||||||
--hash=sha256:07d880053c6461c9b89cd5d4808f3b8336665fa3acdefd6777662c5ed73a851a \
|
|
||||||
--hash=sha256:12500d761ac091f2426567f19f95fd3f15a197d96befb44a5c1e3cbe6db5752c \
|
|
||||||
--hash=sha256:1b540599481c73408f6b392cdffef5b01e8ff7a2ac8caae0a91b8222e88e8f1e \
|
|
||||||
--hash=sha256:35feafe232d1aaf35d51bd42790cbccb882456f9f18cdc411532902370d660df \
|
|
||||||
--hash=sha256:3a7826e68b0cf4ce2c1ee385d64eab7d70e3133171376cac53d7c1790357ec8f \
|
|
||||||
--hash=sha256:46907fa62acaac364fff0b8a9da7b360265d217e4fdeaca0a2397a6883dffba2 \
|
|
||||||
--hash=sha256:4bd4854f0c83aa84a5a40d3b5d0eb1f3c128f4146371e03baed4589fe4f3c931 \
|
|
||||||
--hash=sha256:538fcf6ae856b5e12d13d7da25ad67f02113c96f5989e6ad44422cb5994ca7fc \
|
|
||||||
--hash=sha256:547ebb02031fdada635452250ff39942db8310b5c4a8102dfe9384ee5791e650 \
|
|
||||||
--hash=sha256:5e8b50241dd3c2ed498507f87a6602825073c07f3b7e9560c58411c14fe1e1c9 \
|
|
||||||
--hash=sha256:5fa88e3d5d0b480602553d362c4b33a63e0c40bfea7312a7bf78799e01e0810b \
|
|
||||||
--hash=sha256:68fa227c32240c52982cb931801c5707a7f96dd8927f9102d6c7771ea1ff5698 \
|
|
||||||
--hash=sha256:6ced1ad823ecfa7d3ce26fe8aa4996e2e53fb49b7fed8ad81c80958501ec0619 \
|
|
||||||
--hash=sha256:71b1206e7909792d16933a0d2c1c7f04ae196186c51ba8567abae1d041f06dcb \
|
|
||||||
--hash=sha256:767ef4fa33acda16703725c0473a91e1832d296c37c63896c7153ba81698f1ab \
|
|
||||||
--hash=sha256:7ccfcdfea4fc4b0a02ca2c31de7fcd186beb9cff8207800e14ab66f79c773af6 \
|
|
||||||
--hash=sha256:7e4939ff75149b67aef77980409f156f0082fa36accc475d45c705bb00c6c16a \
|
|
||||||
--hash=sha256:828c9dc9478b34ab96be75c81942d8df0c2bb49edbb481f597314d92b6441d89 \
|
|
||||||
--hash=sha256:8a4e07611997acf178ad13b842377e3d8e9d0a5bac43ece9bfc22a96735d9a4f \
|
|
||||||
--hash=sha256:941a6c2c591da455d760121b44097781bc970be40e0e43081b9139da485ad5b7 \
|
|
||||||
--hash=sha256:9a4af6ed1094f867834f5f07acd1250605a0874169a5fcadbcec864aec2496a6 \
|
|
||||||
--hash=sha256:9ec296f565191f89c48f33d9544d8d82b0d2af7dd7d2d4e6319f27a818f8d1cc \
|
|
||||||
--hash=sha256:9ec95df684583b5596c82bb380c53a603bb051cf019d5c849c47e117c5064395 \
|
|
||||||
--hash=sha256:a04a1836894c8279e5e0a0127c0db8e198ca133d28be8a2a72b4db16f6cf99c1 \
|
|
||||||
--hash=sha256:a3d81165b8474087bb90ec4f333a638ccfd1d69d34a9b4a1a7eaac06648f9fbe \
|
|
||||||
--hash=sha256:b4a247cd3feaae39bb6085fcebf35b3b8ecd9b022db796d89c8f05067ca28e71 \
|
|
||||||
--hash=sha256:ba38cf9984d5462b506e239cf4bc24e84ead4b1d71a3be35e66dad0d13ded7c1 \
|
|
||||||
--hash=sha256:beb57d8a1ca0ae0eb3d08ccaceb77e1a6d93606f0e1754f0d60a6ebd5c288837 \
|
|
||||||
--hash=sha256:d266cd05bd4a95ca1c2b9b5aac50d249cf7c94a542f47e0b22928ddf8b80d1ef \
|
|
||||||
--hash=sha256:d8c3cc6bb76492133474e130a12351a325336c01c96a24aae731abf5a47fe088 \
|
|
||||||
--hash=sha256:db8e62016add2235cc87fb7ea000ede9e4ca0aa1f221b40cef049d02d5d2593d \
|
|
||||||
--hash=sha256:e7507f6c7b0262d3e7b0eeda15045bf5881f4ada70473b87bc7b7c93b992a7d7 \
|
|
||||||
--hash=sha256:ed15edb14f52925869250b1375f0ff58ca5c4fa8adefe4883cfb0737d32f5c02 \
|
|
||||||
--hash=sha256:f57d63a2b5beaf797b87024d018772439f9d3103a395627b77d17a8d72009543 \
|
|
||||||
--hash=sha256:fa5e32c7d9b60b2528108ade2929b115167fe98d59f89555574715054f50fa31 \
|
|
||||||
--hash=sha256:fe79b4ad4836e3da6c4650cb85a663b3a51aef22e1a829c384e18fae87e5e727
|
|
||||||
# via -r requirements-base.in
|
|
||||||
pycparser==2.21 \
|
|
||||||
--hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \
|
|
||||||
--hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206
|
|
||||||
# via cffi
|
|
||||||
pydantic==1.10.2 \
|
|
||||||
--hash=sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42 \
|
|
||||||
--hash=sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624 \
|
|
||||||
--hash=sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e \
|
|
||||||
--hash=sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559 \
|
|
||||||
--hash=sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709 \
|
|
||||||
--hash=sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9 \
|
|
||||||
--hash=sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d \
|
|
||||||
--hash=sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52 \
|
|
||||||
--hash=sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda \
|
|
||||||
--hash=sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912 \
|
|
||||||
--hash=sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c \
|
|
||||||
--hash=sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525 \
|
|
||||||
--hash=sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe \
|
|
||||||
--hash=sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41 \
|
|
||||||
--hash=sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b \
|
|
||||||
--hash=sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283 \
|
|
||||||
--hash=sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965 \
|
|
||||||
--hash=sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c \
|
|
||||||
--hash=sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410 \
|
|
||||||
--hash=sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5 \
|
|
||||||
--hash=sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116 \
|
|
||||||
--hash=sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98 \
|
|
||||||
--hash=sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f \
|
|
||||||
--hash=sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644 \
|
|
||||||
--hash=sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13 \
|
|
||||||
--hash=sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd \
|
|
||||||
--hash=sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254 \
|
|
||||||
--hash=sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6 \
|
|
||||||
--hash=sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488 \
|
|
||||||
--hash=sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5 \
|
|
||||||
--hash=sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c \
|
|
||||||
--hash=sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1 \
|
|
||||||
--hash=sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a \
|
|
||||||
--hash=sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2 \
|
|
||||||
--hash=sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d \
|
|
||||||
--hash=sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236
|
|
||||||
# via inflect
|
|
||||||
pynacl==1.5.0 \
|
|
||||||
--hash=sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858 \
|
|
||||||
--hash=sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d \
|
|
||||||
--hash=sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93 \
|
|
||||||
--hash=sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1 \
|
|
||||||
--hash=sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92 \
|
|
||||||
--hash=sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff \
|
|
||||||
--hash=sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba \
|
|
||||||
--hash=sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394 \
|
|
||||||
--hash=sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b \
|
|
||||||
--hash=sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543
|
|
||||||
# via
|
|
||||||
# -r static-data/official-plugins/wot/requirements.in
|
|
||||||
# onionrblocks
|
|
||||||
pysocks==1.7.1 \
|
|
||||||
--hash=sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299 \
|
|
||||||
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
|
|
||||||
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
|
||||||
# via -r static-data/official-plugins/tor/requirements.in
|
|
||||||
pytz==2022.2.1 \
|
|
||||||
--hash=sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197 \
|
|
||||||
--hash=sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5
|
|
||||||
# via tempora
|
|
||||||
result==0.8.0 \
|
|
||||||
--hash=sha256:c48c909e92181a075ba358228a3fe161e26d205dad416ad81f27f23515a5626d \
|
|
||||||
--hash=sha256:d6a6258f32c057a4e0478999c6ce43dcadaf8ea435f58ac601ae2768f93ef243
|
|
||||||
# via -r requirements-base.in
|
|
||||||
secretstorage==3.3.3 \
|
|
||||||
--hash=sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77 \
|
|
||||||
--hash=sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99
|
|
||||||
# via keyring
|
|
||||||
six==1.16.0 \
|
|
||||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
|
||||||
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
|
|
||||||
# via cheroot
|
|
||||||
stem==1.8.1 \
|
|
||||||
--hash=sha256:81d43a7c668ba9d7bc1103b2e7a911e9d148294b373d27a59ae8da79ef7a3e2f
|
|
||||||
# via -r static-data/official-plugins/tor/requirements.in
|
|
||||||
tempora==5.0.2 \
|
|
||||||
--hash=sha256:31fa5bb33b2641026211f23e808eb8bd351901988b167d45f323c8f450ecf211 \
|
|
||||||
--hash=sha256:e65d32ae68ad772ee738d802689f689b3f883e165e8dadd39aa89ef317b12b99
|
|
||||||
# via portend
|
|
||||||
typing-extensions==4.3.0 \
|
|
||||||
--hash=sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02 \
|
|
||||||
--hash=sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6
|
|
||||||
# via pydantic
|
|
||||||
ujson==5.5.0 \
|
|
||||||
--hash=sha256:0762a4fdf86e01f3f8d8b6b7158d01fdd870799ff3f402b676e358fcd879e7eb \
|
|
||||||
--hash=sha256:10095160dbe6bba8059ad6677a01da251431f4c68041bf796dcac0956b34f8f7 \
|
|
||||||
--hash=sha256:1a485117f97312bef45f5d79d2ff97eff4da503b8a04f3691f59d31141686459 \
|
|
||||||
--hash=sha256:1cef44ea4973344baed3d50a5da4a8843de3a6af7dea7fadf0a594e53ce5892f \
|
|
||||||
--hash=sha256:1dc2f46c31ef22b0aaa28cd71be897bea271e700636658d573df9c43c49ebbd0 \
|
|
||||||
--hash=sha256:21678d7e068707e4d54bdfeb8c250ebc548b51e499aed778b22112ca31a79669 \
|
|
||||||
--hash=sha256:278aa9d7cb56435c96d19f5d702e026bcf69f824e24b41e9b52706abd3565837 \
|
|
||||||
--hash=sha256:2ab011e3556a9a1d9461bd686870c527327765ed02fe53550531d6609a8a33ff \
|
|
||||||
--hash=sha256:2d90414e3b4b44b39825049185959488e084ea7fcaf6124afd5c00893938b09d \
|
|
||||||
--hash=sha256:2e506ecf89b6b9d304362ccef770831ec242a52c89dab1b4aabf1ab0eb1d5ed6 \
|
|
||||||
--hash=sha256:33cd9084fefc74cbacf88c92fd260b61211e00bcde38d640c369e5dc34a2b4e1 \
|
|
||||||
--hash=sha256:3b74467564814fbce322427a5664e6bcc7dae6dbc8acbef76300fe43ca4072ab \
|
|
||||||
--hash=sha256:3f3f4240d99d55eb97cb012e9adf401f5ed9cd827af0341ac44603832202b0d2 \
|
|
||||||
--hash=sha256:3fe1aea596f9539fc20cd9e52f098c842afc090168824fd4ca9744fe13151a03 \
|
|
||||||
--hash=sha256:4a8cb3c8637006c5bd8237ebb5992a76ba06e39988ad5cff2096227443e8fd6a \
|
|
||||||
--hash=sha256:4ef4ab8352861b99bd7fedb1fc6df3ea7f7d5216c789ba6d859e4ea06f1a4c45 \
|
|
||||||
--hash=sha256:5035bb997d163f346c22abcec75190e7e756a5349e7c708bd3d5fd7066a9a854 \
|
|
||||||
--hash=sha256:593a0f6fb0e186c5ba65465ed6f6215a30d1efa898c25e74de1c8577a1bff6d0 \
|
|
||||||
--hash=sha256:59cdcd934385f36e8bd76aedc234371cc75c848d95bdce804ac8aa8744cfeffa \
|
|
||||||
--hash=sha256:5a9b1320d8363a42d857fae8065a2174d38217cdd58cd8dc4f48d54e0591271e \
|
|
||||||
--hash=sha256:5f9681ec4c60d0da590552427d770636d9079038c30b265f507ccde23caa7823 \
|
|
||||||
--hash=sha256:5fd797a4837ba10671954e7c09010cec7aca67e09d193f4920a16beea5f66f65 \
|
|
||||||
--hash=sha256:6019e3480d933d3698f2ecb4b46d64bfadd64e718f04fac36e681f3254b49a93 \
|
|
||||||
--hash=sha256:603607f56a0ee84d9cd2c7e9b1d29b18a70684b94ee34f07b9ffe8dc9c8a9f81 \
|
|
||||||
--hash=sha256:60a4b481978ea2aad8fe8af1ecc271624d01b3cf4b09e9b643dd2fe19c07634c \
|
|
||||||
--hash=sha256:6b9812638d7aa8ecda2e8e1513fb4da999249603bffab7439a5f8f0bb362b0db \
|
|
||||||
--hash=sha256:6c7ae6e0778ab9610f5e80e0595957d101ab8de18c32a8c053a19943ef4831d0 \
|
|
||||||
--hash=sha256:6f83be8257b2f2dd6dea5ee62cd28db90584da7a7af1fba77a2102fc7943638a \
|
|
||||||
--hash=sha256:701e81e047f5c0cffd4ac828efca68b0bd270c616654966a051e9a5f836b385e \
|
|
||||||
--hash=sha256:703fd69d9cb21d6ec2086789df9be2cf8140a76ff127050c24007ea8940dcd3b \
|
|
||||||
--hash=sha256:7471d4486f23518cff343f1eec6c68d1b977ed74c3e6cc3e1ac896b9b7d68645 \
|
|
||||||
--hash=sha256:765d46f3d5e7a1d48075035e2d1a9164f683e3fccde834ca04602e6c588835bc \
|
|
||||||
--hash=sha256:7a09d203983104918c62f2eef9406f24c355511f9217967df23e70fa7f5b54ff \
|
|
||||||
--hash=sha256:7c20cc83b0df47129ec6ed8a47fa7dcfc309c5bad029464004162738502568bb \
|
|
||||||
--hash=sha256:7d7cfac2547c93389fa303fc0c0eb6698825564e8389c41c9b60009c746207b6 \
|
|
||||||
--hash=sha256:7d87c817b292efb748f1974f37e8bb8a8772ef92f05f84e507159360814bcc3f \
|
|
||||||
--hash=sha256:8141f654432cf75144d6103bfac2286b8adf23467201590b173a74535d6be22d \
|
|
||||||
--hash=sha256:849f2ff40264152f25589cb48ddb4a43d14db811f841ec73989bfc0c8c4853fa \
|
|
||||||
--hash=sha256:880c84ce59f49776cf120f77e7ca04877c97c6887917078dbc369eb47004d7cf \
|
|
||||||
--hash=sha256:94874584b733a18b310b0e954d53168e62cd4a0fd9db85b1903f0902a7eb33e8 \
|
|
||||||
--hash=sha256:95603eff711b8f3b9596e1c961dbeb745a792ba1904141612f194e07edd71e5f \
|
|
||||||
--hash=sha256:9585892091ae86045135d6a6129a644142d6a51b23e1428bb5de6d10bc0ce0c7 \
|
|
||||||
--hash=sha256:977bf5be704a88d46bf5b228df8b44521b1f3119d741062191608b3a6a38f224 \
|
|
||||||
--hash=sha256:9cdc46859024501c20ab74ad542cdf2f08b94b5ce384f2f569483fa3ed926d04 \
|
|
||||||
--hash=sha256:a34a5f034b339f69ef7f6a134c22d04b92e07b6ddc1dd65382e7e4ec65d6437d \
|
|
||||||
--hash=sha256:a655f7b755cfc5c07f2116b6dcf0ba148c89adef9a6d40c1b0f1fada878c4345 \
|
|
||||||
--hash=sha256:a7d12f2d2df195c8c4e49d2cdbad640353a856c62ca2c624d8b47aa33b65a2a2 \
|
|
||||||
--hash=sha256:abfe83e082c9208891e2158c1b5044a650ecec408b823bf6bf16cd7f8085cafa \
|
|
||||||
--hash=sha256:b25077a971c7da47bd6846a912a747f6963776d90720c88603b1b55d81790780 \
|
|
||||||
--hash=sha256:bf416a93e1331820c77e3429df26946dbd4fe105e9b487cd2d1b7298b75784a8 \
|
|
||||||
--hash=sha256:c04ae27e076d81a3839047d8eed57c1e17e361640616fd520d752375e3ba8f0c \
|
|
||||||
--hash=sha256:d5bea13c73f36c4346808df3fa806596163a7962b6d28001ca2a391cab856089 \
|
|
||||||
--hash=sha256:d75bef34e69e7effb7b4849e3f830e3174d2cc6ec7273503fdde111c222dc9b3 \
|
|
||||||
--hash=sha256:d93940664a5ccfd79f72dcb939b0c31a3479889f14f0eb95ec52976f8c0cae7d \
|
|
||||||
--hash=sha256:d9c89c521dc90c7564358e525f849b93ad1d710553c1491f66b8cce8113bc901 \
|
|
||||||
--hash=sha256:e0b36257dc90194784531c3b922d8d31fb2b4d8e5adfd27aff4eee7174176365 \
|
|
||||||
--hash=sha256:e1135264bcd40965cd35b0869e36952f54825024befdc7a923df9a7d83cfd800 \
|
|
||||||
--hash=sha256:e510d288e613d6927796dfb728e13e4530fc83b9ccac5888a21f7860486eab21 \
|
|
||||||
--hash=sha256:ee9a2c9a4b2421e77f8fe33ed0621dea03c66c710707553020b1e32f3afb6240 \
|
|
||||||
--hash=sha256:f19f11055ba2961eb39bdb1ff15763a53fca4fa0b5b624da3c7a528e83cdd09c \
|
|
||||||
--hash=sha256:f26544bc10c83a2ff9aa2e093500c1b473f327faae31fb468d591e5823333376 \
|
|
||||||
--hash=sha256:f4875cafc9a6482c04c7df52a725d1c41beb74913c0ff4ec8f189f1954a2afe9 \
|
|
||||||
--hash=sha256:f5179088ef6487c475604b7898731a6ddeeada7702cfb2162155b016703a8475 \
|
|
||||||
--hash=sha256:f63d1ae1ca17bb2c847e298c7bcf084a73d56d434b4c50509fb93a4b4300b0b2 \
|
|
||||||
--hash=sha256:ff4928dc1e9704b567171c16787238201fdbf023665573c12c02146fe1e02eec
|
|
||||||
# via -r requirements-base.in
|
|
||||||
zc-lockfile==2.0 \
|
|
||||||
--hash=sha256:307ad78227e48be260e64896ec8886edc7eae22d8ec53e4d528ab5537a83203b \
|
|
||||||
--hash=sha256:cc33599b549f0c8a248cb72f3bf32d77712de1ff7ee8814312eb6456b42c015f
|
|
||||||
# via cherrypy
|
|
||||||
|
|
||||||
# WARNING: The following packages were not pinned, but pip requires them to be
|
|
||||||
# pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag.
|
|
||||||
# setuptools
|
|
@ -1,8 +1,11 @@
|
|||||||
PyNaCl==1.5.0
|
PyNaCl==1.5.0
|
||||||
|
PySocks==1.7.1
|
||||||
|
stem==1.8.0
|
||||||
|
unpaddedbase32==0.2.0
|
||||||
|
niceware==0.2.1
|
||||||
psutil==5.9.1
|
psutil==5.9.1
|
||||||
filenuke==0.0.0
|
filenuke==0.0.0
|
||||||
ujson==5.4.0
|
ujson==5.4.0
|
||||||
cffi==1.15.1
|
cffi==1.15.1
|
||||||
onionrblocks==7.0.0
|
onionrblocks==7.0.0
|
||||||
ordered-set==4.1.0
|
ordered-set==4.1.0
|
||||||
json-rpc==1.13.0
|
|
40
requirements.txt
Normal file
40
requirements.txt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile with python 3.10
|
||||||
|
# To update, run:
|
||||||
|
#
|
||||||
|
# pip-compile
|
||||||
|
#
|
||||||
|
cffi==1.15.1
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# pynacl
|
||||||
|
filenuke==0.0.0
|
||||||
|
# via -r requirements.in
|
||||||
|
kasten==3.0.0
|
||||||
|
# via onionrblocks
|
||||||
|
mimcvdf==1.2.1
|
||||||
|
# via kasten
|
||||||
|
msgpack==1.0.3
|
||||||
|
# via kasten
|
||||||
|
niceware==0.2.1
|
||||||
|
# via -r requirements.in
|
||||||
|
onionrblocks==7.0.0
|
||||||
|
# via -r requirements.in
|
||||||
|
ordered-set==4.1.0
|
||||||
|
# via -r requirements.in
|
||||||
|
psutil==5.9.1
|
||||||
|
# via -r requirements.in
|
||||||
|
pycparser==2.19
|
||||||
|
# via cffi
|
||||||
|
pynacl==1.5.0
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# onionrblocks
|
||||||
|
pysocks==1.7.1
|
||||||
|
# via -r requirements.in
|
||||||
|
stem==1.8.0
|
||||||
|
# via -r requirements.in
|
||||||
|
ujson==5.4.0
|
||||||
|
# via -r requirements.in
|
||||||
|
unpaddedbase32==0.2.0
|
||||||
|
# via -r requirements.in
|
150
run-onionr-node.py
Executable file
150
run-onionr-node.py
Executable file
@ -0,0 +1,150 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
from threading import Thread
|
||||||
|
from time import sleep
|
||||||
|
from subprocess import DEVNULL
|
||||||
|
|
||||||
|
import ujson
|
||||||
|
from psutil import Popen
|
||||||
|
from psutil import Process
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
sys.path.append(script_dir + '/src/')
|
||||||
|
|
||||||
|
import onionrvalues
|
||||||
|
|
||||||
|
|
||||||
|
sub_script = script_dir + '/' + onionrvalues.SCRIPT_NAME
|
||||||
|
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--bind-address", help="Address to bind to. Be very careful with non-loopback",
|
||||||
|
type=str, default="")
|
||||||
|
parser.add_argument(
|
||||||
|
"--port", help="Port to bind to, must be available and possible",
|
||||||
|
type=int, default=0)
|
||||||
|
parser.add_argument(
|
||||||
|
"--use-bootstrap-file", help="Use bootstrap node list file",
|
||||||
|
type=int, default=1)
|
||||||
|
parser.add_argument(
|
||||||
|
"--show-stats", help="Display curses output of Onionr stats",
|
||||||
|
type=int, default=0)
|
||||||
|
parser.add_argument(
|
||||||
|
"--onboarding", help="Use Onionr onboarding (if first load)",
|
||||||
|
type=int, default=1)
|
||||||
|
parser.add_argument(
|
||||||
|
"--security-level", help="Set Onionr security level",
|
||||||
|
type=int, default=0)
|
||||||
|
parser.add_argument(
|
||||||
|
'--open-ui', help='Open onionr web ui after started',
|
||||||
|
type=int, default=1)
|
||||||
|
parser.add_argument(
|
||||||
|
'--random-localhost-ip', help='bind to random localhost IP for extra security',
|
||||||
|
type=int, default=1)
|
||||||
|
parser.add_argument(
|
||||||
|
'--use-tor', help='Use Tor transport',
|
||||||
|
type=int, default=1)
|
||||||
|
parser.add_argument(
|
||||||
|
'--private-key', help='Use existing private key',
|
||||||
|
type=str, default=0)
|
||||||
|
parser.add_argument(
|
||||||
|
'--animated-background', help='Animated background on webui index. Just for looks.',
|
||||||
|
type=int, default=0)
|
||||||
|
parser.add_argument(
|
||||||
|
'--keep-log-on-exit', help='Onionr can keep or delete its log file on exit',
|
||||||
|
type=int, default=0)
|
||||||
|
parser.add_argument(
|
||||||
|
'--use-upload-mixing', help='Re-upload blocks uploaded to us. Slow but more secure',
|
||||||
|
type=int, default=0)
|
||||||
|
parser.add_argument(
|
||||||
|
'--dev-mode',
|
||||||
|
help='Developer mode makes restarting and testing Onionr less tedious during development',
|
||||||
|
type=int, default=0)
|
||||||
|
parser.add_argument(
|
||||||
|
'--disable-plugin-list',
|
||||||
|
help='plugins to disable by name, separate with commas',
|
||||||
|
type=str, default='chat'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--store-plaintext',
|
||||||
|
help='store plaintext blocks or not. note that encrypted blocks may not really be encrypted, but we cannot detect that',
|
||||||
|
type=int, default=1
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
p = Popen([sub_script, 'version'])
|
||||||
|
p.wait()
|
||||||
|
print("Configuring Onionr before starting daemon")
|
||||||
|
from filepaths import config_file, keys_file
|
||||||
|
from coredb import blockmetadb
|
||||||
|
import onionrcrypto
|
||||||
|
|
||||||
|
|
||||||
|
with open(config_file, 'r') as cf:
|
||||||
|
config = ujson.loads(cf.read())
|
||||||
|
|
||||||
|
|
||||||
|
if args.private_key:
|
||||||
|
priv = args.private_key
|
||||||
|
pub = onionrcrypto.cryptoutils.get_pub_key_from_priv(priv)
|
||||||
|
with open(keys_file, "a") as f:
|
||||||
|
f.write(',' + pub.decode() + ',' + priv)
|
||||||
|
config['general']['public_key'] = pub
|
||||||
|
|
||||||
|
config['plugins']['disabled'] = args.disable_plugin_list.split(',')
|
||||||
|
config['general']['dev_mode'] = False
|
||||||
|
|
||||||
|
config['general']['store_plaintext_blocks'] = True
|
||||||
|
config['general']['use_bootstrap_list'] = True
|
||||||
|
config['transports']['tor'] = True
|
||||||
|
config['general']['bind_port'] = 0 # client api server port
|
||||||
|
config['general']['bind_address'] = '' # client api server address
|
||||||
|
|
||||||
|
if args.bind_address:
|
||||||
|
config['general']['bind_address'] = args.bind_address
|
||||||
|
if args.port:
|
||||||
|
config['client']['client']['port'] = args.port
|
||||||
|
|
||||||
|
if not args.use_bootstrap_file:
|
||||||
|
config['general']['use_bootstrap_list'] = False
|
||||||
|
if not args.store_plaintext:
|
||||||
|
config['general']['store_plaintext_blocks'] = False
|
||||||
|
if args.dev_mode:
|
||||||
|
config['general']['dev_mode'] = True
|
||||||
|
if not args.onboarding:
|
||||||
|
config['onboarding']['done'] = True
|
||||||
|
if not args.random_localhost_ip:
|
||||||
|
config['general']['random_bind_ip'] = False
|
||||||
|
if not args.use_tor:
|
||||||
|
config['transports']['tor'] = False
|
||||||
|
if not args.animated_background:
|
||||||
|
config['ui']['animated_background'] = False
|
||||||
|
if args.keep_log_on_exit:
|
||||||
|
config['log']['file']['remove_on_exit'] = True
|
||||||
|
else:
|
||||||
|
config['log']['file']['remove_on_exit'] = False
|
||||||
|
|
||||||
|
config['general']['upload_mixing'] = False
|
||||||
|
if args.use_upload_mixing:
|
||||||
|
config['general']['upload_mixing'] = True
|
||||||
|
config['general']['display_header'] = False
|
||||||
|
config['general']['security_level'] = args.security_level
|
||||||
|
|
||||||
|
with open(config_file, 'w') as cf:
|
||||||
|
cf.write(ujson.dumps(config, reject_bytes=False))
|
||||||
|
|
||||||
|
if args.open_ui:
|
||||||
|
p = Popen([sub_script, 'start'])
|
||||||
|
sleep(2)
|
||||||
|
Popen([sub_script, 'openhome'])
|
||||||
|
else:
|
||||||
|
p = Popen([sub_script, 'start'])
|
||||||
|
|
||||||
|
p.wait()
|
51
run-onionr-node.sh
Executable file
51
run-onionr-node.sh
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -x
|
||||||
|
ORIG_ONIONR_RUN_DIR=`pwd`
|
||||||
|
export ORIG_ONIONR_RUN_DIR
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
if [[ -n "$ONIONR_DOCKER" ]]; then
|
||||||
|
[[ -f "/privkey" ]] && privkey_opt="--private-key /privkey"
|
||||||
|
[[ -n "$ONIONR_ONBOARDING" ]] || ONIONR_ONBOARDING=0
|
||||||
|
[[ -n "$ONIONR_OPEN_UI" ]] || ONIONR_OPEN_UI=0
|
||||||
|
[[ -n "$ONIONR_RANDOM_LOCALHOST_IP" ]] || ONIONR_RANDOM_LOCALHOST_IP=0
|
||||||
|
[[ -n "$ONIONR_BIND_ADDRESS" ]] || ONIONR_BIND_ADDRESS=0.0.0.0
|
||||||
|
[[ -n "$ONIONR_PORT" ]] || ONIONR_PORT=8080
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -n "$ONIONR_PRIVATE_KEY_FILE" ]] && privkey_opt="--private-key $ONIONR_PRIVATE_KEY_FILE"
|
||||||
|
[[ -n "$ONIONR_USE_BOOTSTRAP_FILE" ]] && bootstrap_opt="--use-bootstrap-file $ONIONR_USE_BOOTSTRAP_FILE"
|
||||||
|
[[ -n "$ONIONR_SHOW_STATS" ]] && show_stats_opt="--show-stats $ONIONR_SHOW_STATS"
|
||||||
|
[[ -n "$ONIONR_ONBOARDING" ]] && onboarding_opt="--onboarding $ONIONR_ONBOARDING"
|
||||||
|
[[ -n "$ONIONR_SECURITY_LEVEL" ]] && security_level_opt="--security-level $ONIONR_SECURITY_LEVEL"
|
||||||
|
[[ -n "$ONIONR_OPEN_UI" ]] && open_ui_opt="--open-ui $ONIONR_OPEN_UI"
|
||||||
|
[[ -n "$ONIONR_RANDOM_LOCALHOST_IP" ]] && random_localhost_ip_opt="--random-localhost-ip $ONIONR_RANDOM_LOCALHOST_IP"
|
||||||
|
[[ -n "$ONIONR_USE_TOR" ]] && use_tor_opt="--use-tor $ONIONR_USE_TOR"
|
||||||
|
[[ -n "$ONIONR_ANIMATED_BACKGROUND" ]] && animated_background_opt="--animated-background $ONIONR_ANIMATED_BACKGROUND"
|
||||||
|
[[ -n "$ONIONR_KEEP_LOG" ]] && keep_log_opt="--keep-log-on-exit $ONIONR_KEEP_LOG"
|
||||||
|
[[ -n "$ONIONR_USE_UPLOAD_MIXING" ]] && use_upload_mixing_opt="--use-upload-mixing $ONIONR_USE_UPLOAD_MIXING"
|
||||||
|
[[ -n "$ONIONR_DEV_MODE" ]] && dev_mode_opt="--dev-mode $ONIONR_DEV_MODE"
|
||||||
|
[[ -n "$ONIONR_DISABLE_PLUGIN_LIST" ]] && disable_plugin_list_opt=" --disable-plugin-list $ONIONR_DISABLE_PLUGIN_LIST"
|
||||||
|
[[ -n "$ONIONR_STORE_PLAINTEXT" ]] && store_plaintext_opt="--store-plaintext $ONIONR_STORE_PLAINTEXT"
|
||||||
|
[[ -n "$ONIONR_BIND_ADDRESS" ]] && bind_address_opt="--bind-address $ONIONR_BIND_ADDRESS"
|
||||||
|
[[ -n "$ONIONR_PORT" ]] && port_opt="--port $ONIONR_PORT"
|
||||||
|
|
||||||
|
|
||||||
|
python3 run-onionr-node.py \
|
||||||
|
$privkey_opt \
|
||||||
|
$bootstrap_opt \
|
||||||
|
$show_stats_opt \
|
||||||
|
$onboarding_opt \
|
||||||
|
$security_level_opt \
|
||||||
|
$open_ui_opt \
|
||||||
|
$random_localhost_ip_opt \
|
||||||
|
$use_tor_opt \
|
||||||
|
$animated_background_opt \
|
||||||
|
$keep_log_opt \
|
||||||
|
$use_upload_mixing_opt \
|
||||||
|
$dev_mode_opt \
|
||||||
|
$disable_plugin_list_opt \
|
||||||
|
$store_plaintext_opt \
|
||||||
|
$bind_address_opt \
|
||||||
|
$port_opt \
|
||||||
|
"$@"
|
33
sandboxed-onionr.py
Normal file
33
sandboxed-onionr.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
import tempfile, shutil
|
||||||
|
import stat
|
||||||
|
|
||||||
|
env_var = "firejailed-onionr"
|
||||||
|
|
||||||
|
def copytree(src, dst, symlinks=False, ignore=None):
|
||||||
|
for item in os.listdir(src):
|
||||||
|
if item in (".git", ".vscode", ".github"):
|
||||||
|
continue
|
||||||
|
s = os.path.join(src, item)
|
||||||
|
d = os.path.join(dst, item)
|
||||||
|
if os.path.isdir(s):
|
||||||
|
shutil.copytree(s, d, symlinks, ignore)
|
||||||
|
else:
|
||||||
|
shutil.copy2(s, d)
|
||||||
|
|
||||||
|
env_var = "firejailed-onionr"
|
||||||
|
directory = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||||
|
|
||||||
|
if not os.getenv(env_var):
|
||||||
|
temp_dir = tempfile.mkdtemp()
|
||||||
|
print(temp_dir)
|
||||||
|
copytree(directory, temp_dir)
|
||||||
|
os.system(f"firejail --env={env_var}={temp_dir} --private={temp_dir} python3 ./sandboxed-onionr.py")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
os.system(f"python3 -m pip install -r ./requirements.txt --user")
|
||||||
|
os.system(f"./onionr.sh start &")
|
||||||
|
|
||||||
|
|
@ -1 +1,7 @@
|
|||||||
This directory contains useful scripts and utilities that don't make sense to include as official Onionr features.
|
This directory contains useful scripts and utilities that don't make sense to include as official Onionr features.
|
||||||
|
|
||||||
|
passphrase-generator.py: very simple utility to generate and print a strong passphrase to stdout. 256 bits of entropy by default.
|
||||||
|
enable-dev-config.py/disable-dev-config.py: enable/disable dev default config setup
|
||||||
|
block-spammer.py: attack tool for spamming blocks
|
||||||
|
announce-attack.py: flood a node with false nodes
|
||||||
|
run-unit-test-by-name: runs a unit test (no browser, runtime or intgegration test) by name
|
51
scripts/client-api-request-crafter.py
Normal file
51
scripts/client-api-request-crafter.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""Craft and send requests to the local client API"""
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
if not os.path.exists('onionr.sh'):
|
||||||
|
os.chdir('../')
|
||||||
|
sys.path.append("src/")
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import readline
|
||||||
|
|
||||||
|
histfile = os.path.join(os.path.expanduser("~"), ".onionr_history")
|
||||||
|
try:
|
||||||
|
readline.read_history_file(histfile)
|
||||||
|
# default history len is -1 (infinite), which may grow unruly
|
||||||
|
readline.set_history_length(1000)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
atexit.register(readline.write_history_file, histfile)
|
||||||
|
from onionrutils.localcommand import local_command
|
||||||
|
from onionrutils.localcommand import get_hostname
|
||||||
|
|
||||||
|
try:
|
||||||
|
print('API file found, probably running on ' + get_hostname())
|
||||||
|
except TypeError:
|
||||||
|
print('Onionr not running')
|
||||||
|
sys.exit(1)
|
||||||
|
print('1. get request (default)')
|
||||||
|
print('2. post request')
|
||||||
|
choice = input(">").lower().strip()
|
||||||
|
post = False
|
||||||
|
post_data = {}
|
||||||
|
json = False
|
||||||
|
endpoint = input("URL Endpoint: ")
|
||||||
|
data = input("Data url param: ")
|
||||||
|
if choice in ("2", "post", "post request"):
|
||||||
|
post = True
|
||||||
|
print("Enter post data")
|
||||||
|
post_data = input()
|
||||||
|
if post_data:
|
||||||
|
print("Is this JSON?")
|
||||||
|
json = input("y/n").lower().strip()
|
||||||
|
if json == "y":
|
||||||
|
json = True
|
||||||
|
|
||||||
|
ret = local_command(endpoint, data=data, post=post, post_data=post_data, is_json=json)
|
||||||
|
print("Response: \n", ret)
|
@ -11,9 +11,12 @@ conf['tor']['existing_control_port'] = 0
|
|||||||
conf['tor']['existing_control_password'] = ""
|
conf['tor']['existing_control_password'] = ""
|
||||||
conf['tor']['existing_socks_port'] = 0
|
conf['tor']['existing_socks_port'] = 0
|
||||||
|
|
||||||
|
conf['general']['dev_mode'] = False
|
||||||
conf['general']['insert_deniable_blocks'] = True
|
conf['general']['insert_deniable_blocks'] = True
|
||||||
conf['general']['random_bind_ip'] = True
|
conf['general']['random_bind_ip'] = True
|
||||||
conf['general']['display_header'] = True
|
conf['general']['display_header'] = True
|
||||||
|
conf['general']['security_level'] = 0
|
||||||
|
conf['general']['use_bootstrap_list'] = True
|
||||||
conf['onboarding']['done'] = False
|
conf['onboarding']['done'] = False
|
||||||
conf['general']['minimum_block_pow'] = 5
|
conf['general']['minimum_block_pow'] = 5
|
||||||
conf['general']['minimum_send_pow'] = 5
|
conf['general']['minimum_send_pow'] = 5
|
||||||
|
@ -10,6 +10,7 @@ conf = json.load(open('static-data/default_config.json', 'r'))
|
|||||||
|
|
||||||
block_pow = int(input("Block POW level:"))
|
block_pow = int(input("Block POW level:"))
|
||||||
|
|
||||||
|
conf['general']['security_level'] = int(input("Security level:"))
|
||||||
conf['transports']['tor'] = False
|
conf['transports']['tor'] = False
|
||||||
if input('Use Tor? y/n').lower() == 'y':
|
if input('Use Tor? y/n').lower() == 'y':
|
||||||
conf['transports']['tor'] = True
|
conf['transports']['tor'] = True
|
||||||
@ -19,11 +20,15 @@ if input('Use Tor? y/n').lower() == 'y':
|
|||||||
conf['tor']['existing_control_password'] = input("Tor pass:")
|
conf['tor']['existing_control_password'] = input("Tor pass:")
|
||||||
conf['tor']['existing_socks_port'] = int(input("Existing socks port:"))
|
conf['tor']['existing_socks_port'] = int(input("Existing socks port:"))
|
||||||
|
|
||||||
|
conf['general']['dev_mode'] = True
|
||||||
conf['general']['insert_deniable_blocks'] = False
|
conf['general']['insert_deniable_blocks'] = False
|
||||||
conf['general']['random_bind_ip'] = False
|
conf['general']['random_bind_ip'] = False
|
||||||
conf['onboarding']['done'] = True
|
conf['onboarding']['done'] = True
|
||||||
conf['general']['minimum_block_pow'] = block_pow
|
conf['general']['minimum_block_pow'] = block_pow
|
||||||
conf['general']['minimum_send_pow'] = block_pow
|
conf['general']['minimum_send_pow'] = block_pow
|
||||||
|
conf['general']['use_bootstrap_list'] = False
|
||||||
|
if input("Use bootstrap list? y/n").lower() == 'y':
|
||||||
|
conf['general']['use_bootstrap_list'] = True
|
||||||
conf['log']['file']['remove_on_exit'] = False
|
conf['log']['file']['remove_on_exit'] = False
|
||||||
conf['ui']['animated_background'] = False
|
conf['ui']['animated_background'] = False
|
||||||
if input('Stat reporting? y/n') == 'y':
|
if input('Stat reporting? y/n') == 'y':
|
||||||
|
41
scripts/generate-onions.py
Executable file
41
scripts/generate-onions.py
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import stem
|
||||||
|
from stem import process
|
||||||
|
from stem.control import Controller
|
||||||
|
if not os.path.exists('onionr.sh'):
|
||||||
|
os.chdir('../')
|
||||||
|
sys.path.append("src/")
|
||||||
|
|
||||||
|
try:
|
||||||
|
sys.argv[1]
|
||||||
|
except IndexError:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
tor_process = process.launch_tor_with_config(
|
||||||
|
completion_percent=0,
|
||||||
|
config = {
|
||||||
|
'ControlPort': '2778',
|
||||||
|
'DisableNetwork': '1',
|
||||||
|
'Log': [
|
||||||
|
'NOTICE stdout',
|
||||||
|
'ERR file /tmp/tor_error_log',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with Controller.from_port('127.0.0.1', 2778) as controller:
|
||||||
|
controller.authenticate()
|
||||||
|
for i in range(1024, 1024 + int(sys.argv[1])):
|
||||||
|
hs = controller.create_ephemeral_hidden_service(
|
||||||
|
{80: i},
|
||||||
|
key_type='NEW',
|
||||||
|
key_content='ED25519-V3',
|
||||||
|
await_publication=False,
|
||||||
|
detached=True)
|
||||||
|
print(hs.service_id + ".onion")
|
||||||
|
controller.remove_ephemeral_hidden_service(hs.service_id)
|
||||||
|
|
||||||
|
tor_process.kill()
|
21
scripts/passphrase-generator.py
Executable file
21
scripts/passphrase-generator.py
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""Generate a 16 word passphase with 256 bits of entropy.
|
||||||
|
|
||||||
|
Specify true to reduce to 128 bits"""
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import niceware
|
||||||
|
|
||||||
|
byte_count = 32 # 256 bits of entropy with niceware
|
||||||
|
|
||||||
|
arg = False
|
||||||
|
try:
|
||||||
|
arg = sys.argv[1].lower()
|
||||||
|
if arg == 'true':
|
||||||
|
byte_count = 16
|
||||||
|
except IndexError: pass
|
||||||
|
|
||||||
|
print(' '.join(niceware.generate_passphrase(byte_count)))
|
50
scripts/remote-ui.py
Executable file
50
scripts/remote-ui.py
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""Craft and send requests to the local client API"""
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from threading import Thread
|
||||||
|
if not os.path.exists('onionr.sh'):
|
||||||
|
os.chdir('../')
|
||||||
|
sys.path.append("src/")
|
||||||
|
|
||||||
|
import filepaths
|
||||||
|
import config
|
||||||
|
config.reload()
|
||||||
|
|
||||||
|
with open(filepaths.private_API_host_file, 'r') as host:
|
||||||
|
hostname = host.read()
|
||||||
|
|
||||||
|
port = config.get("client.client.port", 0)
|
||||||
|
if not port:
|
||||||
|
print("Could not get port for Onionr UI. Try again")
|
||||||
|
sys.exit(1)
|
||||||
|
torrc = f"""
|
||||||
|
HiddenServiceDir remote-onionr-hs
|
||||||
|
HiddenServicePort 80 {hostname}:{port}
|
||||||
|
"""
|
||||||
|
|
||||||
|
with open("remote-onionr-torrc", "w") as torrc_f:
|
||||||
|
torrc_f.write(torrc)
|
||||||
|
|
||||||
|
|
||||||
|
def show_onion():
|
||||||
|
while True:
|
||||||
|
time.sleep(1)
|
||||||
|
try:
|
||||||
|
with open("remote-onionr-hs/hostname", "r") as f:
|
||||||
|
o = f.read()
|
||||||
|
print("UI Onion (Keep secret):", o)
|
||||||
|
config.set("ui.public_remote_enabled", True)
|
||||||
|
config.set("ui.public_remote_hosts", [o])
|
||||||
|
config.save()
|
||||||
|
break
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
Thread(target=show_onion, daemon=True).start()
|
||||||
|
|
||||||
|
os.system("tor -f remote-onionr-torrc")
|
16
scripts/show-blocks.py
Normal file
16
scripts/show-blocks.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import stem
|
||||||
|
|
||||||
|
if not os.path.exists('onionr.sh'):
|
||||||
|
os.chdir('../')
|
||||||
|
sys.path.append("src/")
|
||||||
|
from coredb.blockmetadb import get_block_list
|
||||||
|
from onionrblocks.onionrblockapi import Block
|
||||||
|
|
||||||
|
for bl in get_block_list():
|
||||||
|
bl_obj = Block(bl, decrypt=False)
|
||||||
|
b_type = bl_obj.getType()
|
||||||
|
if not b_type:
|
||||||
|
b_type = "encrypted"
|
||||||
|
print(bl + " - " + str(bl_obj.date) + " - " + b_type)
|
40
scripts/sybil-attack.py
Normal file
40
scripts/sybil-attack.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import stem
|
||||||
|
|
||||||
|
if not os.path.exists('onionr.sh'):
|
||||||
|
os.chdir('../')
|
||||||
|
sys.path.append("src/")
|
||||||
|
from onionrutils import stringvalidators
|
||||||
|
from onionrutils import basicrequests
|
||||||
|
|
||||||
|
from stem.control import Controller
|
||||||
|
|
||||||
|
onionr_ip = input("onionr ip address: ")
|
||||||
|
onionr_port = int(input("Enter onionr public api port: "))
|
||||||
|
|
||||||
|
controller = Controller.from_port('127.0.0.1', int(input("Enter tor controller port: ")))
|
||||||
|
controller.authenticate()
|
||||||
|
|
||||||
|
node = input("Enter node to attack. Note that you legally must use your own, and even that might lead to technical or legal issues: ")
|
||||||
|
assert stringvalidators.validate_transport(node)
|
||||||
|
|
||||||
|
socks = input("Socks:")
|
||||||
|
|
||||||
|
adders = set([])
|
||||||
|
for i in range(int(input("Sybil addresses: "))):
|
||||||
|
response = controller.create_ephemeral_hidden_service({80: f'{onionr_ip}:{onionr_port}'}, await_publication=True)
|
||||||
|
#print(i, response.service_id)
|
||||||
|
adders.add(response.service_id)
|
||||||
|
|
||||||
|
|
||||||
|
for x in adders:
|
||||||
|
x += '.onion'
|
||||||
|
print(f"Introducing {x} to {node}")
|
||||||
|
basicrequests.do_post_request(
|
||||||
|
f'http://{node}/announce',
|
||||||
|
data = {'node': x},
|
||||||
|
port=socks)
|
||||||
|
|
||||||
|
|
||||||
|
|
12
scripts/testblock.py
Executable file
12
scripts/testblock.py
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
if not os.path.exists('onionr.sh'):
|
||||||
|
os.chdir('../')
|
||||||
|
sys.path.append("src/")
|
||||||
|
import onionrblocks
|
||||||
|
|
||||||
|
expire = 600
|
||||||
|
print(onionrblocks.insert(data=os.urandom(32), expire=expire))
|
||||||
|
|
@ -15,16 +15,15 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
import cProfile
|
|
||||||
import threading
|
|
||||||
|
|
||||||
onionr_profiling = os.getenv("ONIONR_PROFILING", False)
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import sqlite3
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
sys.stderr.write(
|
||||||
|
'Error, Onionr requires Sqlite3-enabled Python.\n' +
|
||||||
|
'https://stackoverflow.com/a/1875095\n')
|
||||||
|
sys.exit(1)
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -44,29 +43,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import locale # noqa
|
import locale # noqa
|
||||||
locale.setlocale(locale.LC_ALL, '') # noqa
|
locale.setlocale(locale.LC_ALL, '') # noqa
|
||||||
|
|
||||||
import traceback
|
|
||||||
import signal
|
|
||||||
|
|
||||||
def sigusr_stacktrace(signum, frame):
|
|
||||||
"""Print stacktrace on SIGUSR1"""
|
|
||||||
stack_trace_file = os.getenv('ONIONR_STACK_TRACE_FILE', False)
|
|
||||||
for th in threading.enumerate():
|
|
||||||
if stack_trace_file:
|
|
||||||
with open(stack_trace_file, 'a') as f:
|
|
||||||
f.write(f'Thread: {th.name}')
|
|
||||||
f.write('-' * 44 + '')
|
|
||||||
traceback.print_stack(sys._current_frames()[th.ident], file=f)
|
|
||||||
else:
|
|
||||||
print(f'Thread: {th.name}')
|
|
||||||
print('-' * 44)
|
|
||||||
traceback.print_stack(sys._current_frames()[th.ident])
|
|
||||||
#print(traceback.format_stack(frame))
|
|
||||||
|
|
||||||
signal.signal(signal.SIGUSR1, sigusr_stacktrace)
|
|
||||||
|
|
||||||
# Importing initailzes logging
|
|
||||||
from logger import log as logging
|
|
||||||
|
|
||||||
ran_as_script = False
|
ran_as_script = False
|
||||||
if __name__ == "__main__": ran_as_script = True
|
if __name__ == "__main__": ran_as_script = True
|
||||||
|
|
||||||
@ -97,6 +73,7 @@ if sys.version_info[0] == 2 or sys.version_info[1] < min_ver:
|
|||||||
from utils import createdirs
|
from utils import createdirs
|
||||||
createdirs.create_dirs()
|
createdirs.create_dirs()
|
||||||
|
|
||||||
|
import bigbrother # noqa
|
||||||
from onionrcommands import parser # noqa
|
from onionrcommands import parser # noqa
|
||||||
from onionrplugins import onionrevents as events # noqa
|
from onionrplugins import onionrevents as events # noqa
|
||||||
|
|
||||||
@ -106,21 +83,23 @@ import config # noqa
|
|||||||
from utils import identifyhome # noqa
|
from utils import identifyhome # noqa
|
||||||
import filepaths # noqa
|
import filepaths # noqa
|
||||||
|
|
||||||
|
if config.get('advanced.security_auditing', True):
|
||||||
|
try:
|
||||||
|
bigbrother.enable_ministries()
|
||||||
|
except onionrexceptions.PythonVersion:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def onionr_main():
|
def onionr_main():
|
||||||
"""Onionr entrypoint, start command processor
|
"""Onionr entrypoint, start command processor
|
||||||
|
|
||||||
Entrypoint for daemon is commands/daemonlaunch/__init__.py
|
Entrypoint for daemon is commands/daemonlaunch/__init__.py
|
||||||
"""
|
"""
|
||||||
events.event('beforecmdparsing', threaded=False)
|
|
||||||
parser.register()
|
parser.register()
|
||||||
|
|
||||||
|
|
||||||
if ran_as_script:
|
if ran_as_script:
|
||||||
if onionr_profiling:
|
onionr_main()
|
||||||
cProfile.run('onionr_main()')
|
|
||||||
else:
|
|
||||||
onionr_main()
|
|
||||||
|
|
||||||
config.reload()
|
config.reload()
|
||||||
|
|
||||||
|
@ -3,11 +3,10 @@
|
|||||||
Processes interpreter hook events to detect security leaks
|
Processes interpreter hook events to detect security leaks
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
import os
|
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from logger import log as logging
|
from onionrexceptions import PythonVersion
|
||||||
|
from . import ministry
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -22,11 +21,15 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
|
|
||||||
import ministry
|
|
||||||
|
|
||||||
plugin_name = 'bigbrother'
|
|
||||||
PLUGIN_VERSION = '0.0.0'
|
def _auditing_supported():
|
||||||
|
try:
|
||||||
|
sys.audit
|
||||||
|
sys.addaudithook
|
||||||
|
except AttributeError:
|
||||||
|
raise PythonVersion('Auditing not supported interpreter')
|
||||||
|
|
||||||
|
|
||||||
def sys_hook_entrypoint(event, info):
|
def sys_hook_entrypoint(event, info):
|
||||||
"""Entrypoint for big brother sys auditors."""
|
"""Entrypoint for big brother sys auditors."""
|
||||||
@ -44,10 +47,5 @@ def sys_hook_entrypoint(event, info):
|
|||||||
def enable_ministries(disable_hooks: Iterable = None):
|
def enable_ministries(disable_hooks: Iterable = None):
|
||||||
"""Enable auditors."""
|
"""Enable auditors."""
|
||||||
disable_hooks = disable_hooks or []
|
disable_hooks = disable_hooks or []
|
||||||
|
_auditing_supported() # raises PythonVersion exception if <3.8
|
||||||
sys.addaudithook(sys_hook_entrypoint)
|
sys.addaudithook(sys_hook_entrypoint)
|
||||||
|
|
||||||
|
|
||||||
def on_init(api, data=None):
|
|
||||||
enable_ministries()
|
|
||||||
logging.info(
|
|
||||||
"Big brother enabled, blocking unsafe Python code.")
|
|
@ -4,7 +4,7 @@ Ensure sockets don't get made to non localhost
|
|||||||
"""
|
"""
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
from onionrexceptions import NetworkLeak
|
from onionrexceptions import NetworkLeak
|
||||||
"""
|
"""
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
@ -37,12 +37,12 @@ def detect_socket_leaks(socket_event):
|
|||||||
try:
|
try:
|
||||||
ip_address = ipaddress.ip_address(ip_address)
|
ip_address = ipaddress.ip_address(ip_address)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logging.warn(f'Conn made to {ip_address} outside of Tor/similar')
|
logger.warn(f'Conn made to {ip_address} outside of Tor/similar')
|
||||||
raise \
|
raise \
|
||||||
NetworkLeak('Conn to host/non local IP, this is a privacy issue!')
|
NetworkLeak('Conn to host/non local IP, this is a privacy issue!')
|
||||||
|
|
||||||
# Validate that the IP is localhost ipv4
|
# Validate that the IP is localhost ipv4
|
||||||
if not ip_address.is_loopback and not ip_address.is_multicast \
|
if not ip_address.is_loopback and not ip_address.is_multicast \
|
||||||
and not ip_address.is_private:
|
and not ip_address.is_private:
|
||||||
logging.warn(f'Conn made to {ip_address} outside of Tor/similar')
|
logger.warn(f'Conn made to {ip_address} outside of Tor/similar')
|
||||||
raise NetworkLeak('Conn to non local IP, this is a privacy concern!')
|
raise NetworkLeak('Conn to non local IP, this is a privacy concern!')
|
@ -3,7 +3,7 @@
|
|||||||
Log (not block) read/write of non-user data files and non-python lib files
|
Log (not block) read/write of non-user data files and non-python lib files
|
||||||
"""
|
"""
|
||||||
from utils.identifyhome import identify_home
|
from utils.identifyhome import identify_home
|
||||||
from logger import log as logging
|
import logger
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -33,4 +33,4 @@ def detect_disk_access(info):
|
|||||||
|
|
||||||
if identify_home() not in info[0]:
|
if identify_home() not in info[0]:
|
||||||
if 'proc' not in info[0]: # if it is, it is onionr stats
|
if 'proc' not in info[0]: # if it is, it is onionr stats
|
||||||
logging.warn(f'[DISK MINISTRY] {info}')
|
logger.warn(f'[DISK MINISTRY] {info}')
|
87
src/bigbrother/ministry/ofexec.py
Normal file
87
src/bigbrother/ministry/ofexec.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
Prevent eval/exec/os.system and log it
|
||||||
|
"""
|
||||||
|
import base64
|
||||||
|
import platform
|
||||||
|
|
||||||
|
import logger
|
||||||
|
from utils import identifyhome
|
||||||
|
from onionrexceptions import ArbitraryCodeExec
|
||||||
|
"""
|
||||||
|
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/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
untrusted_exec = True
|
||||||
|
|
||||||
|
def block_system(cmd):
|
||||||
|
"""Prevent os.system except for whitelisted commands+contexts."""
|
||||||
|
logger.warn('POSSIBLE EXPLOIT DETECTED, SEE LOGS', terminal=True)
|
||||||
|
logger.warn(f'POSSIBLE EXPLOIT: shell command not in whitelist: {cmd}')
|
||||||
|
raise ArbitraryCodeExec('os.system command not in whitelist')
|
||||||
|
|
||||||
|
|
||||||
|
def block_exec(event, info):
|
||||||
|
"""Prevent arbitrary code execution in eval/exec and log it."""
|
||||||
|
# because libraries have stupid amounts of compile/exec/eval,
|
||||||
|
# We have to use a whitelist where it can be tolerated
|
||||||
|
# Generally better than nothing, not a silver bullet
|
||||||
|
if untrusted_exec:
|
||||||
|
return
|
||||||
|
whitelisted_code = [
|
||||||
|
'netrc.py',
|
||||||
|
'shlex.py',
|
||||||
|
'gzip.py',
|
||||||
|
'<werkzeug routing>',
|
||||||
|
'werkzeug/test.py',
|
||||||
|
'multiprocessing/popen_fork.py',
|
||||||
|
'multiprocessing/util.py',
|
||||||
|
'multiprocessing/connection.py',
|
||||||
|
'multiprocessing/queues.py',
|
||||||
|
'multiprocessing/synchronize.py',
|
||||||
|
'onionrutils/escapeansi.py',
|
||||||
|
'stem/connection.py',
|
||||||
|
'stem/response/add_onion.py',
|
||||||
|
'stem/response/authchallenge.py',
|
||||||
|
'stem/response/getinfo.py',
|
||||||
|
'stem/response/getconf.py',
|
||||||
|
'stem/response/mapaddress.py',
|
||||||
|
'stem/response/protocolinfo.py',
|
||||||
|
'apport/__init__.py',
|
||||||
|
'apport/report.py'
|
||||||
|
]
|
||||||
|
whitelisted_source = [
|
||||||
|
'ZABaAGQBZAJsAW0CWgIBAHoGZAFkA2wDWgRXAG4LBABlBXkZAQABAAEAZARaBlkAbgN3AGQFWgZkAWQGbAdtCFoIAQBkAWQDbAlaCWQBZAdsCm0LWgwBAAkAZQmgDWQIZAWhAnM1ZARaBmUIgwBkCRcAWg5kEmQMZQ9kDWUPZgRkDmQPhAVaEGQTZBBkEYQBWhFkA1MA',
|
||||||
|
'RwBkAGQBhABkAYMCWgBkAlMA',
|
||||||
|
'ZABaAGQBZAJsAVoBZAFkAmwCWgJkAWQCbANaA2QBZAJsBFoEZAFkAmwFWgVkAWQCbAZaBmQBZAJsB1oIZAFkAmwJWghkAWQCbApaCGQBZAJsC1oIZANaDGQEWg1kBWQCZAJkBmQCZQ1kB2QIZAJmCWQJZAqEAVoOZAVkBmQCZQ1kB2QIZgZkC2QMhAFaD2QCUwA=',
|
||||||
|
'ZABaAGQBZAJsAVoBZAFkAmwCWgJkAWQCbANaA2QBZAJsBFoEZAFkAmwFWgVkAWQCbAZaBmQBZAJsB1oIZAFkAmwJWghkAWQCbApaCGQBZANsC20MWgxtDVoNbQ5aDm0PWg9tEFoQbRFaEQEAZARaEmQFWhNkBmQCZAJkB2QCZRNkCGQJZAJmCWQKZRRkC2UPZRBlFBkAGQBkDGUPZRQZAGQNZRVkDmUPZQ1lFGcBZAJmAhkAGQBkD2UVZBBlFmQRZRZkEmUPZRQZAGQTZQRqF2YUZBRkFYQFWhhkBmQHZAJlE2QIZAlmBmQWZQ5lFGURZRRlEGUUGQBmAhkAZgIZAGQKZRRkDWUVZA5lD2UNZRRnAWQCZgIZABkAZA9lFWQQZRZkEWUWZBNlBGoXZhBkF2QYhAVaGWQCUwA='
|
||||||
|
]
|
||||||
|
home = identifyhome.identify_home()
|
||||||
|
|
||||||
|
code_b64 = base64.b64encode(info[0].co_code).decode()
|
||||||
|
if code_b64 in whitelisted_source:
|
||||||
|
return
|
||||||
|
|
||||||
|
for source in whitelisted_code:
|
||||||
|
if info[0].co_filename.endswith(source):
|
||||||
|
return
|
||||||
|
|
||||||
|
if 'plugins/' in info[0].co_filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.warn('POSSIBLE EXPLOIT DETECTED, SEE LOGS', terminal=True)
|
||||||
|
logger.warn('POSSIBLE EXPLOIT DETECTED: ' + info[0].co_filename)
|
||||||
|
logger.warn('Prevented exec/eval. Report this with the sample below')
|
||||||
|
logger.warn(f'{event} code in base64 format: {code_b64}')
|
||||||
|
raise ArbitraryCodeExec("Arbitrary code (eval/exec) detected.")
|
@ -1,7 +1,6 @@
|
|||||||
from typing import Callable, Generator, List
|
from typing import Callable, Generator, List
|
||||||
|
|
||||||
from onionrblocks import Block
|
from onionrblocks import Block
|
||||||
from onionrplugins import onionrevents
|
|
||||||
|
|
||||||
import db
|
import db
|
||||||
|
|
||||||
@ -16,9 +15,8 @@ block_storage_observers: List[Callable] = []
|
|||||||
|
|
||||||
|
|
||||||
def add_block_to_db(block: Block):
|
def add_block_to_db(block: Block):
|
||||||
onionrevents.event('before_block_db_add', block, threaded=False)
|
# Raises db.DuplicateKey if dupe
|
||||||
db.set_if_new(block_db_path, block.id, block.raw) # Raises db.DuplicateKey if dupe
|
db.set_if_new(block_db_path, block.id, block.raw)
|
||||||
onionrevents.event('after_block_db_add', block, threaded=False)
|
|
||||||
|
|
||||||
|
|
||||||
def has_block(block_hash):
|
def has_block(block_hash):
|
||||||
|
@ -2,7 +2,7 @@ from typing import Set
|
|||||||
|
|
||||||
from onionrblocks import Block
|
from onionrblocks import Block
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
|
|
||||||
from .deleteblock import delete_block
|
from .deleteblock import delete_block
|
||||||
from .getblocks import get_blocks_after_timestamp
|
from .getblocks import get_blocks_after_timestamp
|
||||||
@ -18,7 +18,7 @@ def clean_block_database():
|
|||||||
Block(block.id, block.raw, auto_verify=True)
|
Block(block.id, block.raw, auto_verify=True)
|
||||||
except ValueError: # block expired
|
except ValueError: # block expired
|
||||||
remove_set.add(block)
|
remove_set.add(block)
|
||||||
|
|
||||||
if len(remove_set):
|
if len(remove_set):
|
||||||
logging.info(f"Cleaning {len(remove_set)} blocks")
|
logger.info(f"Cleaning {len(remove_set)} blocks", terminal=True)
|
||||||
[i for i in map(delete_block, remove_set)]
|
[i for i in map(delete_block, remove_set)]
|
||||||
|
@ -6,12 +6,7 @@ from onionrblocks import Block
|
|||||||
|
|
||||||
from .dbpath import block_db_path
|
from .dbpath import block_db_path
|
||||||
|
|
||||||
|
|
||||||
def get_blocks_by_type(block_type: str) -> "Generator[Block]":
|
def get_blocks_by_type(block_type: str) -> "Generator[Block]":
|
||||||
try:
|
|
||||||
block_type = block_type.decode('utf-8')
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
block_db = db.get_db_obj(block_db_path, 'u')
|
block_db = db.get_db_obj(block_db_path, 'u')
|
||||||
for block_hash in db.list_keys(block_db_path):
|
for block_hash in db.list_keys(block_db_path):
|
||||||
block = Block(block_hash, block_db[block_hash], auto_verify=False)
|
block = Block(block_hash, block_db[block_hash], auto_verify=False)
|
||||||
@ -30,4 +25,4 @@ def get_blocks_after_timestamp(
|
|||||||
if block_type == block.type:
|
if block_type == block.type:
|
||||||
yield block
|
yield block
|
||||||
else:
|
else:
|
||||||
yield block
|
yield block
|
@ -6,9 +6,10 @@ import os
|
|||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
|
|
||||||
import ujson as json
|
import ujson as json
|
||||||
from logger import log as logging
|
import logger
|
||||||
import filepaths
|
import filepaths
|
||||||
|
|
||||||
|
from . import onboarding
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -106,7 +107,7 @@ def save():
|
|||||||
with open(get_config_file(), 'w', encoding="utf8") as configfile:
|
with open(get_config_file(), 'w', encoding="utf8") as configfile:
|
||||||
json.dump(get_config(), configfile, indent=2)
|
json.dump(get_config(), configfile, indent=2)
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
logging.warn('Failed to write to configuration file.')
|
logger.warn('Failed to write to configuration file.')
|
||||||
|
|
||||||
|
|
||||||
def reload():
|
def reload():
|
||||||
@ -117,7 +118,7 @@ def reload():
|
|||||||
set_config(json.loads(configfile.read()))
|
set_config(json.loads(configfile.read()))
|
||||||
except (FileNotFoundError, JSONDecodeError) as e:
|
except (FileNotFoundError, JSONDecodeError) as e:
|
||||||
pass
|
pass
|
||||||
#logging.debug('Failed to parse configuration file.')
|
#logger.debug('Failed to parse configuration file.')
|
||||||
|
|
||||||
|
|
||||||
def get_config():
|
def get_config():
|
||||||
|
79
src/config/onboarding.py
Normal file
79
src/config/onboarding.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
Setup config from onboarding choices
|
||||||
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from filepaths import onboarding_mark_file
|
||||||
|
from onionrtypes import JSONSerializable
|
||||||
|
from onionrtypes import OnboardingConfig
|
||||||
|
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/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _get_val_or_none(json: dict, key: str) -> Union[None, JSONSerializable]:
|
||||||
|
try:
|
||||||
|
return json['configInfo'][key]
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def set_config_from_onboarding(config_settings: OnboardingConfig):
|
||||||
|
get = _get_val_or_none
|
||||||
|
|
||||||
|
config.reload()
|
||||||
|
|
||||||
|
if get(config_settings, 'optimize'):
|
||||||
|
config.set('ui.animated_background', False)
|
||||||
|
config.set('general.insert_deniable_blocks', False)
|
||||||
|
|
||||||
|
if get(config_settings, 'stateTarget') or not get(config_settings,
|
||||||
|
'networkContrib'):
|
||||||
|
config.set('general.security_level', 1)
|
||||||
|
|
||||||
|
if get(config_settings, 'localThreat'):
|
||||||
|
config.set('general.security_level', 3)
|
||||||
|
config.set('transports.lan', False)
|
||||||
|
|
||||||
|
config.set('ui.theme', 'light')
|
||||||
|
if get(config_settings, 'useDark'):
|
||||||
|
config.set('ui.theme', 'dark')
|
||||||
|
|
||||||
|
disabled = config.get('plugins.disabled', [])
|
||||||
|
|
||||||
|
if not get(config_settings, 'circles') or \
|
||||||
|
config.get('general.security_level') > 0:
|
||||||
|
disabled.append('flow')
|
||||||
|
|
||||||
|
if not get(config_settings, 'mail'):
|
||||||
|
disabled.append('pms')
|
||||||
|
|
||||||
|
config.set('plugins.disabled', disabled)
|
||||||
|
|
||||||
|
config.set('general.store_plaintext_blocks',
|
||||||
|
get(config_settings, 'plainContrib'))
|
||||||
|
|
||||||
|
config.set('onboarding.done', True, savefile=True)
|
||||||
|
|
||||||
|
|
||||||
|
def set_onboarding_finished():
|
||||||
|
"""Create the onboarding completed setting file"""
|
||||||
|
Path(onboarding_mark_file).touch()
|
||||||
|
|
||||||
|
|
||||||
|
def is_onboarding_finished() -> bool:
|
||||||
|
return True
|
1
src/coredb/__init__.py
Normal file
1
src/coredb/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import keydb
|
9
src/coredb/dbfiles.py
Normal file
9
src/coredb/dbfiles.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from utils import identifyhome
|
||||||
|
import filepaths
|
||||||
|
home = identifyhome.identify_home()
|
||||||
|
if not home.endswith('/'): home += '/'
|
||||||
|
|
||||||
|
address_info_db = '%saddress.db' % (home,)
|
||||||
|
user_id_info_db = '%susers.db' % (home,)
|
||||||
|
forward_keys_db = '%sforward-keys.db' % (home,)
|
||||||
|
blacklist_db = '%sblacklist.db' % (home,)
|
1
src/coredb/keydb/__init__.py
Normal file
1
src/coredb/keydb/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import addkeys, listkeys, removekeys, userinfo
|
55
src/coredb/keydb/addkeys.py
Normal file
55
src/coredb/keydb/addkeys.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
add user keys or transport addresses
|
||||||
|
"""
|
||||||
|
import sqlite3
|
||||||
|
from onionrutils import stringvalidators
|
||||||
|
from . import listkeys
|
||||||
|
from .. import dbfiles
|
||||||
|
import onionrcrypto
|
||||||
|
import onionrvalues
|
||||||
|
"""
|
||||||
|
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 add_pub_key(peerID, name=''):
|
||||||
|
"""Add a public key to the key database (misleading function name)."""
|
||||||
|
if peerID in listkeys.list_pub_keys() or peerID == onionrcrypto.pub_key:
|
||||||
|
raise ValueError("specified id is already known")
|
||||||
|
|
||||||
|
# This function simply adds a peer to the DB
|
||||||
|
if not stringvalidators.validate_pub_key(peerID):
|
||||||
|
return False
|
||||||
|
|
||||||
|
conn = sqlite3.connect(dbfiles.user_id_info_db, timeout=onionrvalues.DATABASE_LOCK_TIMEOUT)
|
||||||
|
hashID = ""
|
||||||
|
c = conn.cursor()
|
||||||
|
t = (peerID, name, 'unknown', hashID, 0)
|
||||||
|
|
||||||
|
for i in c.execute("SELECT * FROM peers WHERE id = ?;", (peerID,)):
|
||||||
|
try:
|
||||||
|
if i[0] == peerID:
|
||||||
|
conn.close()
|
||||||
|
return False
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
c.execute('INSERT INTO peers (id, name, dateSeen, hashID, trust) VALUES(?, ?, ?, ?, ?);', t)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
63
src/coredb/keydb/listkeys.py
Normal file
63
src/coredb/keydb/listkeys.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
"""
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
get lists for user keys
|
||||||
|
"""
|
||||||
|
import sqlite3
|
||||||
|
import logger
|
||||||
|
from onionrutils import epoch
|
||||||
|
import onionrvalues
|
||||||
|
from .. import dbfiles
|
||||||
|
from . import userinfo
|
||||||
|
"""
|
||||||
|
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 list_pub_keys(randomOrder=True, getPow=False, trust=0):
|
||||||
|
"""
|
||||||
|
Return a list of public keys (misleading function name)
|
||||||
|
|
||||||
|
randomOrder determines if the list should be in a random order
|
||||||
|
trust sets the minimum trust to list
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(dbfiles.user_id_info_db, timeout=onionrvalues.DATABASE_LOCK_TIMEOUT)
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
payload = ''
|
||||||
|
|
||||||
|
if trust not in (0, 1, 2):
|
||||||
|
logger.error('Tried to select invalid trust.')
|
||||||
|
return
|
||||||
|
|
||||||
|
if randomOrder:
|
||||||
|
payload = 'SELECT * FROM peers WHERE trust >= ? ORDER BY RANDOM();'
|
||||||
|
else:
|
||||||
|
payload = 'SELECT * FROM peers WHERE trust >= ?;'
|
||||||
|
|
||||||
|
peer_list = []
|
||||||
|
|
||||||
|
for i in c.execute(payload, (trust,)):
|
||||||
|
try:
|
||||||
|
if len(i[0]) != 0:
|
||||||
|
if getPow:
|
||||||
|
peer_list.append(i[0] + '-' + i[1])
|
||||||
|
else:
|
||||||
|
peer_list.append(i[0])
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return peer_list
|
43
src/coredb/keydb/removekeys.py
Normal file
43
src/coredb/keydb/removekeys.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
"""
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
Remove a transport address but don't ban them
|
||||||
|
"""
|
||||||
|
import sqlite3
|
||||||
|
from onionrplugins import onionrevents as events
|
||||||
|
from onionrutils import stringvalidators
|
||||||
|
from onionrutils import mnemonickeys
|
||||||
|
from .. import dbfiles
|
||||||
|
import onionrvalues
|
||||||
|
"""
|
||||||
|
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 remove_user(pubkey: str)->bool:
|
||||||
|
"""Remove a user from the user database"""
|
||||||
|
pubkey = mnemonickeys.get_base32(pubkey)
|
||||||
|
if stringvalidators.validate_pub_key(pubkey):
|
||||||
|
conn = sqlite3.connect(
|
||||||
|
dbfiles.user_id_info_db,
|
||||||
|
timeout=onionrvalues.DATABASE_LOCK_TIMEOUT)
|
||||||
|
c = conn.cursor()
|
||||||
|
t = (pubkey,)
|
||||||
|
c.execute('Delete from peers where id=?;', t)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
74
src/coredb/keydb/userinfo.py
Normal file
74
src/coredb/keydb/userinfo.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
"""
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
get or set information about a user id
|
||||||
|
"""
|
||||||
|
import sqlite3
|
||||||
|
from .. import dbfiles
|
||||||
|
import onionrvalues
|
||||||
|
|
||||||
|
"""
|
||||||
|
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_user_info(peer, info):
|
||||||
|
"""
|
||||||
|
Get info about a peer from their database entry
|
||||||
|
|
||||||
|
id text 0
|
||||||
|
name text, 1
|
||||||
|
adders text, 2
|
||||||
|
dateSeen not null, 3
|
||||||
|
trust int 4
|
||||||
|
hashID text 5
|
||||||
|
"""
|
||||||
|
conn = sqlite3.connect(dbfiles.user_id_info_db, timeout=onionrvalues.DATABASE_LOCK_TIMEOUT)
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
command = (peer,)
|
||||||
|
infoNumbers = {'id': 0, 'name': 1, 'adders': 2, 'dateSeen': 3, 'trust': 4, 'hashID': 5}
|
||||||
|
info = infoNumbers[info]
|
||||||
|
iterCount = 0
|
||||||
|
retVal = ''
|
||||||
|
|
||||||
|
for row in c.execute('SELECT * FROM peers WHERE id=?;', command):
|
||||||
|
for i in row:
|
||||||
|
if iterCount == info:
|
||||||
|
retVal = i
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
iterCount += 1
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return retVal
|
||||||
|
|
||||||
|
def set_peer_info(peer, key, data):
|
||||||
|
"""
|
||||||
|
Update a peer for a key
|
||||||
|
"""
|
||||||
|
|
||||||
|
conn = sqlite3.connect(dbfiles.user_id_info_db, timeout=onionrvalues.DATABASE_LOCK_TIMEOUT)
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
command = (data, peer)
|
||||||
|
|
||||||
|
if key not in ('id', 'name', 'pubkey', 'forwardKey', 'dateSeen', 'trust'):
|
||||||
|
raise ValueError("Got invalid database key when setting peer info")
|
||||||
|
|
||||||
|
c.execute('UPDATE peers SET ' + key + ' = ? WHERE id=?', command)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
set_user_info = set_peer_info
|
@ -7,8 +7,36 @@ app_root = os.path.dirname(os.path.realpath(__file__)) + '/../../'
|
|||||||
|
|
||||||
gossip_server_socket_file = home + 'gossip-server.sock'
|
gossip_server_socket_file = home + 'gossip-server.sock'
|
||||||
|
|
||||||
|
usage_file = home + 'disk-usage.txt'
|
||||||
|
contacts_location = home + 'contacts/'
|
||||||
|
public_API_host_file = home + 'public-host.txt'
|
||||||
|
private_API_host_file = home + 'private-host.txt'
|
||||||
|
bootstrap_file_location = 'static-data/bootstrap-nodes.txt'
|
||||||
|
data_nonce_file = home + 'block-nonces.dat'
|
||||||
|
forward_keys_file = home + 'forward-keys.db'
|
||||||
|
cached_storage = home + 'cachedstorage.dat'
|
||||||
|
export_location = home + 'block-export/'
|
||||||
|
upload_list = home + 'upload-list.json'
|
||||||
config_file = home + 'config.json'
|
config_file = home + 'config.json'
|
||||||
|
daemon_mark_file = home + '/daemon-true.txt'
|
||||||
lock_file = home + 'onionr.lock'
|
lock_file = home + 'onionr.lock'
|
||||||
pid_file = home + 'onionr.pid'
|
pid_file = home + 'onionr.pid'
|
||||||
|
|
||||||
|
site_cache = home + 'onionr-sites.txt'
|
||||||
|
|
||||||
|
tor_hs_loc = home + 'hs/'
|
||||||
|
tor_hs_address_file = home + 'hs/hostname'
|
||||||
|
|
||||||
|
data_nonce_file = home + 'block-nonces.dat'
|
||||||
|
|
||||||
|
keys_file = home + 'keys.txt'
|
||||||
|
|
||||||
|
onboarding_mark_file = home + 'onboarding-completed'
|
||||||
|
|
||||||
log_file = home + 'onionr.log'
|
log_file = home + 'onionr.log'
|
||||||
|
|
||||||
|
ephemeral_services_file = home + 'ephemeral-services.list'
|
||||||
|
|
||||||
|
restarting_indicator = home + "is-restarting"
|
||||||
|
|
||||||
|
block_database = home + "blocks.db"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import threading
|
import threading
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import TYPE_CHECKING, Set, Tuple
|
from typing import TYPE_CHECKING, Set, Tuple
|
||||||
from logger import log as logging
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ordered_set import OrderedSet
|
from ordered_set import OrderedSet
|
||||||
@ -12,6 +11,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
from onionrthreads import add_onionr_thread
|
from onionrthreads import add_onionr_thread
|
||||||
import onionrplugins
|
import onionrplugins
|
||||||
|
import logger
|
||||||
|
|
||||||
from .connectpeer import connect_peer
|
from .connectpeer import connect_peer
|
||||||
from .client import start_gossip_client
|
from .client import start_gossip_client
|
||||||
@ -56,4 +56,4 @@ def start_gossip_threads():
|
|||||||
sleep(60)
|
sleep(60)
|
||||||
if len(gossip_peer_set):
|
if len(gossip_peer_set):
|
||||||
return
|
return
|
||||||
logging.error("Could not connect to any peers :(")
|
logger.error("Could not connect to any peers :(", terminal=True)
|
||||||
|
@ -21,7 +21,7 @@ if TYPE_CHECKING:
|
|||||||
from ..peer import Peer
|
from ..peer import Peer
|
||||||
from ordered_set import OrderedSet
|
from ordered_set import OrderedSet
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
import config
|
import config
|
||||||
import onionrplugins
|
import onionrplugins
|
||||||
from ..commands import GossipCommands
|
from ..commands import GossipCommands
|
||||||
@ -59,22 +59,22 @@ def block_queue_processing():
|
|||||||
|
|
||||||
|
|
||||||
while not len(gossip_peer_set):
|
while not len(gossip_peer_set):
|
||||||
sleep(1)
|
sleep(0.2)
|
||||||
if dandelion_phase.remaining_time() <= 15:
|
if dandelion_phase.remaining_time() <= 15:
|
||||||
#logging.debug("Sleeping")
|
#logger.debug("Sleeping", terminal=True)
|
||||||
sleep(dandelion_phase.remaining_time())
|
sleep(dandelion_phase.remaining_time())
|
||||||
if dandelion_phase.is_stem_phase() and config.get('security.dandelion.enabled', True):
|
if dandelion_phase.is_stem_phase() and config.get('security.dandelion.enabled', True):
|
||||||
logging.debug("Entering stem phase")
|
logger.debug("Entering stem phase", terminal=True)
|
||||||
try:
|
try:
|
||||||
# Stem out blocks for (roughly) remaining epoch time
|
# Stem out blocks for (roughly) remaining epoch time
|
||||||
asyncio.run(stem_out(dandelion_phase))
|
asyncio.run(stem_out(dandelion_phase))
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.error(traceback.format_exc())
|
logger.error(traceback.format_exc(), terminal=True)
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
#logging.debug("Entering fluff phase")
|
#logger.debug("Entering fluff phase", terminal=True)
|
||||||
# Add block to primary block db, where the diffuser can read it
|
# Add block to primary block db, where the diffuser can read it
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
store_blocks(dandelion_phase)
|
store_blocks(dandelion_phase)
|
||||||
@ -101,7 +101,7 @@ def start_gossip_client():
|
|||||||
# transport plugin handles the new peer
|
# transport plugin handles the new peer
|
||||||
add_onionr_thread(
|
add_onionr_thread(
|
||||||
get_new_peers,
|
get_new_peers,
|
||||||
120, 'get_new_peers', initial_sleep=120)
|
60, 'get_new_peers', initial_sleep=120)
|
||||||
|
|
||||||
# Start a new thread to stream blocks from peers
|
# Start a new thread to stream blocks from peers
|
||||||
# These blocks are being diffused and are stored in
|
# These blocks are being diffused and are stored in
|
||||||
|
@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .. import Peer
|
from .. import Peer
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
import onionrplugins
|
import onionrplugins
|
||||||
|
|
||||||
from ..commands import GossipCommands, command_to_byte
|
from ..commands import GossipCommands, command_to_byte
|
||||||
@ -28,7 +28,7 @@ def do_announce():
|
|||||||
sock.sendall(command_to_byte(GossipCommands.ANNOUNCE))
|
sock.sendall(command_to_byte(GossipCommands.ANNOUNCE))
|
||||||
sock.sendall(our_transport_address)
|
sock.sendall(our_transport_address)
|
||||||
if int.from_bytes(sock.recv(1), 'big') != 1:
|
if int.from_bytes(sock.recv(1), 'big') != 1:
|
||||||
logging.warn(
|
logger.warn(
|
||||||
f"Could not announce with {announce_peer.transport_address}")
|
f"Could not announce with {announce_peer.transport_address}")
|
||||||
sock.close()
|
sock.close()
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from ordered_set import OrderedSet
|
|||||||
import config
|
import config
|
||||||
from onionrthreads import add_delayed_thread
|
from onionrthreads import add_delayed_thread
|
||||||
from blockdb import add_block_to_db
|
from blockdb import add_block_to_db
|
||||||
from logger import log as logging
|
import logger
|
||||||
|
|
||||||
from ...constants import BLACKHOLE_EVADE_TIMER_SECS, OUTBOUND_DANDELION_EDGES
|
from ...constants import BLACKHOLE_EVADE_TIMER_SECS, OUTBOUND_DANDELION_EDGES
|
||||||
from ...commands import GossipCommands, command_to_byte
|
from ...commands import GossipCommands, command_to_byte
|
||||||
@ -45,9 +45,9 @@ async def _setup_edge(
|
|||||||
try:
|
try:
|
||||||
s = peer.get_socket(12)
|
s = peer.get_socket(12)
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
logging.debug(f"{peer.transport_address} timed out when trying stemout")
|
logger.debug(f"{peer.transport_address} timed out when trying stemout")
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.debug(traceback.format_exc())
|
logger.debug(traceback.format_exc())
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -56,18 +56,18 @@ async def _setup_edge(
|
|||||||
if s.recv(1) == dandelion.StemAcceptResult.DENY:
|
if s.recv(1) == dandelion.StemAcceptResult.DENY:
|
||||||
raise StemConnectionDenied
|
raise StemConnectionDenied
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Peer timed out when establishing stem connection")
|
"Peer timed out when establishing stem connection", terminal=True)
|
||||||
logging.debug(traceback.format_exc())
|
logger.debug(traceback.format_exc())
|
||||||
except StemConnectionDenied:
|
except StemConnectionDenied:
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Stem connection denied (peer has too many) " +
|
"Stem connection denied (peer has too many) " +
|
||||||
f"{peer.transport_address}")
|
f"{peer.transport_address}")
|
||||||
logging.debug(traceback.format_exc())
|
logger.debug(traceback.format_exc())
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warn(
|
logger.warn(
|
||||||
"Error asking peer to establish stem connection" +
|
"Error asking peer to establish stem connection" +
|
||||||
traceback.format_exc())
|
traceback.format_exc(), terminal=True)
|
||||||
else:
|
else:
|
||||||
# Return peer socket if it is in stem reception mode successfully
|
# Return peer socket if it is in stem reception mode successfully
|
||||||
return s
|
return s
|
||||||
@ -113,10 +113,10 @@ async def stem_out(d_phase: 'DandelionPhase'):
|
|||||||
await _setup_edge(gossip_peer_set, tried_edges))
|
await _setup_edge(gossip_peer_set, tried_edges))
|
||||||
except NotEnoughEdges:
|
except NotEnoughEdges:
|
||||||
# No possible edges at this point (edges < OUTBOUND_DANDELION_EDGE)
|
# No possible edges at this point (edges < OUTBOUND_DANDELION_EDGE)
|
||||||
#logging.debug(
|
#logger.debug(
|
||||||
# "Making too few edges for stemout " +
|
# "Making too few edges for stemout " +
|
||||||
# "this is bad for anonymity if frequent.",
|
# "this is bad for anonymity if frequent.",
|
||||||
# )
|
# terminal=True)
|
||||||
if strict_dandelion:
|
if strict_dandelion:
|
||||||
not_enough_edges = True
|
not_enough_edges = True
|
||||||
else:
|
else:
|
||||||
@ -133,10 +133,10 @@ async def stem_out(d_phase: 'DandelionPhase'):
|
|||||||
else:
|
else:
|
||||||
# Ran out of time for stem phase
|
# Ran out of time for stem phase
|
||||||
if not d_phase.is_stem_phase() or d_phase.remaining_time() < 5:
|
if not d_phase.is_stem_phase() or d_phase.remaining_time() < 5:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Did not stem out any blocks in time, " +
|
"Did not stem out any blocks in time, " +
|
||||||
"if this happens regularly you may be under attack",
|
"if this happens regularly you may be under attack",
|
||||||
)
|
terminal=False)
|
||||||
for s in peer_sockets:
|
for s in peer_sockets:
|
||||||
if s:
|
if s:
|
||||||
s.close()
|
s.close()
|
||||||
@ -155,7 +155,7 @@ async def stem_out(d_phase: 'DandelionPhase'):
|
|||||||
except Empty:
|
except Empty:
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warn(traceback.format_exc())
|
logger.warn(traceback.format_exc())
|
||||||
else:
|
else:
|
||||||
# stream routine exited early
|
# stream routine exited early
|
||||||
pass
|
pass
|
||||||
|
@ -4,7 +4,7 @@ from queue import Empty
|
|||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
from ...constants import BLOCK_ID_SIZE, BLOCK_MAX_SIZE, BLOCK_SIZE_LEN
|
from ...constants import BLOCK_ID_SIZE, BLOCK_MAX_SIZE, BLOCK_SIZE_LEN
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -22,7 +22,7 @@ async def do_stem_stream(
|
|||||||
remaining_time = d_phase.remaining_time()
|
remaining_time = d_phase.remaining_time()
|
||||||
my_phase_id = d_phase.phase_id
|
my_phase_id = d_phase.phase_id
|
||||||
|
|
||||||
|
|
||||||
while remaining_time > 1 and my_phase_id == d_phase.phase_id:
|
while remaining_time > 1 and my_phase_id == d_phase.phase_id:
|
||||||
# Primary client component that communicate's with gossip.server.acceptstem
|
# Primary client component that communicate's with gossip.server.acceptstem
|
||||||
remaining_time = d_phase.remaining_time()
|
remaining_time = d_phase.remaining_time()
|
||||||
@ -35,7 +35,7 @@ async def do_stem_stream(
|
|||||||
await sleep(1)
|
await sleep(1)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
logging.info("Sending block over dandelion++")
|
logger.info("Sending block over dandelion++", terminal=True)
|
||||||
|
|
||||||
block_size = str(len(bl.raw)).zfill(BLOCK_SIZE_LEN)
|
block_size = str(len(bl.raw)).zfill(BLOCK_SIZE_LEN)
|
||||||
def _send_it():
|
def _send_it():
|
||||||
|
@ -6,7 +6,9 @@ if TYPE_CHECKING:
|
|||||||
from socket import socket
|
from socket import socket
|
||||||
|
|
||||||
from onionrplugins import onionrevents
|
from onionrplugins import onionrevents
|
||||||
from logger import log as logging
|
import logger
|
||||||
|
|
||||||
|
from socks import GeneralProxyError
|
||||||
|
|
||||||
from ..peer import Peer
|
from ..peer import Peer
|
||||||
from ..commands import GossipCommands, command_to_byte
|
from ..commands import GossipCommands, command_to_byte
|
||||||
@ -21,9 +23,12 @@ def _do_ask_peer(peer):
|
|||||||
try:
|
try:
|
||||||
_ask_peer(peer)
|
_ask_peer(peer)
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
logging.debug("Timed out when asking for new peers")
|
logger.debug("Timed out when asking for new peers")
|
||||||
|
except GeneralProxyError:
|
||||||
|
logger.debug("Proxy error")
|
||||||
|
logger.debug(format_exc(), terminal=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.error(format_exc())
|
logger.error(format_exc(), terminal=True)
|
||||||
|
|
||||||
def _ask_peer(peer):
|
def _ask_peer(peer):
|
||||||
s: 'socket' = peer.get_socket(12)
|
s: 'socket' = peer.get_socket(12)
|
||||||
@ -44,14 +49,14 @@ def _ask_peer(peer):
|
|||||||
'address': peer,
|
'address': peer,
|
||||||
'callback': connectpeer.connect_peer
|
'callback': connectpeer.connect_peer
|
||||||
}
|
}
|
||||||
#logging.info("Got new peer from exchange " + peer.decode('utf-8'))
|
logger.info("Got new peer from exchange " + peer.decode('utf-8'), terminal=True)
|
||||||
onionrevents.event('announce_rec', data=connect_data, threaded=True)
|
onionrevents.event('announce_rec', data=connect_data, threaded=True)
|
||||||
s.close()
|
s.close()
|
||||||
|
|
||||||
|
|
||||||
def get_new_peers():
|
def get_new_peers():
|
||||||
if not len(gossip_peer_set):
|
if not len(gossip_peer_set):
|
||||||
logging.debug("Peer set empty, cannot get new peers")
|
logger.debug("Peer set empty, cannot get new peers")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Deep copy the peer list
|
# Deep copy the peer list
|
||||||
@ -74,7 +79,7 @@ def get_new_peers():
|
|||||||
threads = []
|
threads = []
|
||||||
for peer in peers_we_ask:
|
for peer in peers_we_ask:
|
||||||
t = Thread(
|
t = Thread(
|
||||||
target=_do_ask_peer,
|
target=_do_ask_peer,
|
||||||
args=[peer], daemon=True, name='_do_ask_peer')
|
args=[peer], daemon=True, name='_do_ask_peer')
|
||||||
t.start()
|
t.start()
|
||||||
threads.append(t)
|
threads.append(t)
|
||||||
|
@ -3,7 +3,6 @@ from threading import Thread
|
|||||||
from queue import Queue
|
from queue import Queue
|
||||||
from queue import Empty
|
from queue import Empty
|
||||||
|
|
||||||
from onionrplugins.onionrevents import event
|
|
||||||
import blockdb
|
import blockdb
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -33,9 +32,9 @@ def store_blocks(dandelion_phase: 'DandelionPhase'):
|
|||||||
while not dandelion_phase.is_stem_phase() \
|
while not dandelion_phase.is_stem_phase() \
|
||||||
and dandelion_phase.remaining_time() > 1:
|
and dandelion_phase.remaining_time() > 1:
|
||||||
try:
|
try:
|
||||||
bl = new_queue.get(timeout=dandelion_phase.remaining_time() + 1)
|
blockdb.add_block_to_db(
|
||||||
blockdb.add_block_to_db(bl)
|
new_queue.get(timeout=dandelion_phase.remaining_time() + 1)
|
||||||
event('gotblock', data=bl, threaded=True)
|
)
|
||||||
event(f'gotblock{bl.type}', data=bl, threaded=True)
|
|
||||||
except Empty:
|
except Empty:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
from ordered_set import OrderedSet
|
from ordered_set import OrderedSet
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
|
|
||||||
import onionrblocks
|
import onionrblocks
|
||||||
from ...peerset import gossip_peer_set
|
from ...peerset import gossip_peer_set
|
||||||
@ -67,11 +67,11 @@ def stream_from_peers():
|
|||||||
stream_times = 100
|
stream_times = 100
|
||||||
try:
|
try:
|
||||||
sock = peer.get_socket(CONNECT_TIMEOUT)
|
sock = peer.get_socket(CONNECT_TIMEOUT)
|
||||||
except (TimeoutError, ConnectionRefusedError) as _:
|
except ConnectionRefusedError:
|
||||||
need_socket_lock.release()
|
need_socket_lock.release()
|
||||||
return
|
return
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warn(traceback.format_exc())
|
logger.warn(traceback.format_exc(), terminal=True)
|
||||||
need_socket_lock.release()
|
need_socket_lock.release()
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
@ -83,7 +83,7 @@ def stream_from_peers():
|
|||||||
|
|
||||||
while stream_times >= stream_counter:
|
while stream_times >= stream_counter:
|
||||||
stream_counter += 1
|
stream_counter += 1
|
||||||
#logging.debug("Reading block of id in stream with " + peer.transport_address)
|
logger.debug("Reading block of id in stream with " + peer.transport_address, terminal=True)
|
||||||
sock.settimeout(5)
|
sock.settimeout(5)
|
||||||
block_id = sock.recv(BLOCK_ID_SIZE)
|
block_id = sock.recv(BLOCK_ID_SIZE)
|
||||||
if blockdb.has_block(block_id):
|
if blockdb.has_block(block_id):
|
||||||
@ -91,22 +91,22 @@ def stream_from_peers():
|
|||||||
continue
|
continue
|
||||||
sock.sendall(int(1).to_bytes(1, 'big'))
|
sock.sendall(int(1).to_bytes(1, 'big'))
|
||||||
|
|
||||||
#logging.debug("Reading block size in stream")
|
#logger.debug("Reading block size in stream", terminal=True)
|
||||||
|
|
||||||
sock.settimeout(5)
|
sock.settimeout(5)
|
||||||
block_size = int(sock.recv(BLOCK_SIZE_LEN))
|
block_size = int(sock.recv(BLOCK_SIZE_LEN))
|
||||||
if block_size > BLOCK_MAX_SIZE or block_size <= 0:
|
if block_size > BLOCK_MAX_SIZE or block_size <= 0:
|
||||||
logging.warn(
|
logger.warn(
|
||||||
f"Peer {peer.transport_address} " +
|
f"Peer {peer.transport_address} " +
|
||||||
"reported block size out of range")
|
"reported block size out of range")
|
||||||
break
|
break
|
||||||
|
|
||||||
sock.settimeout(5)
|
sock.settimeout(5)
|
||||||
block_data = sock.recv(block_size)
|
block_data = sock.recv(block_size)
|
||||||
|
|
||||||
#logging.debug(
|
#logger.debug(
|
||||||
# "We got a block from stream, assuming it is valid",
|
# "We got a block from stream, assuming it is valid",
|
||||||
# )
|
# terminal=True)
|
||||||
try:
|
try:
|
||||||
blockdb.add_block_to_db(
|
blockdb.add_block_to_db(
|
||||||
onionrblocks.Block(
|
onionrblocks.Block(
|
||||||
@ -118,20 +118,19 @@ def stream_from_peers():
|
|||||||
raise
|
raise
|
||||||
# Tell them to keep streaming
|
# Tell them to keep streaming
|
||||||
sock.sendall(int(1).to_bytes(1, 'big'))
|
sock.sendall(int(1).to_bytes(1, 'big'))
|
||||||
except (BrokenPipeError, TimeoutError, ConnectionError) as e:
|
except (BrokenPipeError, TimeoutError) as e:
|
||||||
pass
|
pass
|
||||||
#logging.debug(f"{e} when streaming from peers")
|
#logger.debug(f"{e} when streaming from peers", terminal=True)
|
||||||
#logging.debug(traceback.format_exc())
|
#logger.debug(traceback.format_exc())
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warn(traceback.format_exc())
|
logger.warn(traceback.format_exc(), terminal=True)
|
||||||
finally:
|
finally:
|
||||||
sock.close()
|
sock.close()
|
||||||
need_socket_lock.release()
|
need_socket_lock.release()
|
||||||
|
|
||||||
# spawn stream threads infinitely
|
# spawn stream threads infinitely
|
||||||
while True:
|
while True:
|
||||||
if not gossip_peer_set:
|
|
||||||
sleep(2)
|
|
||||||
available_set = gossip_peer_set - tried_peers
|
available_set = gossip_peer_set - tried_peers
|
||||||
if not len(available_set) and len(tried_peers):
|
if not len(available_set) and len(tried_peers):
|
||||||
try:
|
try:
|
||||||
@ -158,4 +157,4 @@ def stream_from_peers():
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
need_socket_lock.release()
|
need_socket_lock.release()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import traceback
|
|||||||
from gossip.commands import GossipCommands, command_to_byte
|
from gossip.commands import GossipCommands, command_to_byte
|
||||||
from .peerset import gossip_peer_set
|
from .peerset import gossip_peer_set
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
|
|
||||||
|
|
||||||
def connect_peer(peer):
|
def connect_peer(peer):
|
||||||
@ -11,12 +11,12 @@ def connect_peer(peer):
|
|||||||
try:
|
try:
|
||||||
s = peer.get_socket(120)
|
s = peer.get_socket(120)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warn(f"Could not connect to {peer.transport_address}")
|
logger.warn(f"Could not connect to {peer.transport_address}")
|
||||||
logging.warn(traceback.format_exc())
|
logger.warn(traceback.format_exc())
|
||||||
else:
|
else:
|
||||||
with s:
|
with s:
|
||||||
s.sendall(command_to_byte(GossipCommands.PING))
|
s.sendall(command_to_byte(GossipCommands.PING))
|
||||||
|
|
||||||
if s.recv(4).decode('utf-8') == 'PONG':
|
if s.recv(4).decode('utf-8') == 'PONG':
|
||||||
gossip_peer_set.add(peer)
|
gossip_peer_set.add(peer)
|
||||||
logging.info(f"connected to {peer.transport_address}")
|
logger.info(f"connected to {peer.transport_address}", terminal=True)
|
||||||
|
@ -12,7 +12,7 @@ from gossip import constants
|
|||||||
from ..connectpeer import connect_peer
|
from ..connectpeer import connect_peer
|
||||||
|
|
||||||
from onionrplugins import onionrevents
|
from onionrplugins import onionrevents
|
||||||
from logger import log as logging
|
import logger
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from onionrblocks import Block
|
from onionrblocks import Block
|
||||||
@ -88,9 +88,9 @@ def gossip_server():
|
|||||||
try:
|
try:
|
||||||
await diffuse_blocks(reader, writer)
|
await diffuse_blocks(reader, writer)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warn(
|
logger.warn(
|
||||||
f"Err streaming blocks\n{traceback.format_exc()}",
|
f"Err streaming blocks\n{traceback.format_exc()}",
|
||||||
)
|
terminal=True)
|
||||||
case GossipCommands.PUT_BLOCKS:
|
case GossipCommands.PUT_BLOCKS:
|
||||||
# Pick block queue & append stemmed blocks to it
|
# Pick block queue & append stemmed blocks to it
|
||||||
try:
|
try:
|
||||||
@ -99,18 +99,18 @@ def gossip_server():
|
|||||||
inbound_dandelion_edge_count)
|
inbound_dandelion_edge_count)
|
||||||
except asyncio.exceptions.TimeoutError:
|
except asyncio.exceptions.TimeoutError:
|
||||||
pass
|
pass
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Inbound edge timed out when steming blocks to us",
|
"Inbound edge timed out when steming blocks to us",
|
||||||
)
|
terminal=True)
|
||||||
except asyncio.exceptions.IncompleteReadError:
|
except asyncio.exceptions.IncompleteReadError:
|
||||||
pass
|
pass
|
||||||
logging.debug(
|
logger.debug(
|
||||||
"Inbound edge timed out (Incomplete Read) when steming blocks to us",
|
"Inbound edge timed out (Incomplete Read) when steming blocks to us",
|
||||||
)
|
terminal=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warn(
|
logger.warn(
|
||||||
f"Err accepting stem blocks\n{traceback.format_exc()}",
|
f"Err accepting stem blocks\n{traceback.format_exc()}",
|
||||||
)
|
terminal=True)
|
||||||
# Subtract dandelion edge, make sure >=0
|
# Subtract dandelion edge, make sure >=0
|
||||||
inbound_dandelion_edge_count[0] = \
|
inbound_dandelion_edge_count[0] = \
|
||||||
max(inbound_dandelion_edge_count[0] - 1, 0)
|
max(inbound_dandelion_edge_count[0] - 1, 0)
|
||||||
@ -120,13 +120,14 @@ def gossip_server():
|
|||||||
if blockdb.has_block(block_id):
|
if blockdb.has_block(block_id):
|
||||||
writer.write(int(0).to_bytes(1, 'big'))
|
writer.write(int(0).to_bytes(1, 'big'))
|
||||||
else:
|
else:
|
||||||
|
|
||||||
writer.write(int(1).to_bytes(1, 'big'))
|
writer.write(int(1).to_bytes(1, 'big'))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
block_size = int(await asyncio.wait_for(reader.readexactly(constants.BLOCK_SIZE_LEN), 30))
|
block_size = int(await asyncio.wait_for(reader.readexactly(constants.BLOCK_SIZE_LEN), 30))
|
||||||
block_data = await reader.readexactly(block_size)
|
block_data = await reader.readexactly(block_size)
|
||||||
|
|
||||||
Thread(
|
Thread(
|
||||||
target=add_block_to_db,
|
target=add_block_to_db,
|
||||||
args=[
|
args=[
|
||||||
Block(block_id, block_data, auto_verify=True)]
|
Block(block_id, block_data, auto_verify=True)]
|
||||||
).start()
|
).start()
|
||||||
@ -136,7 +137,6 @@ def gossip_server():
|
|||||||
await writer.drain()
|
await writer.drain()
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
pass
|
pass
|
||||||
writer.close()
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import traceback
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import List
|
from typing import List
|
||||||
import secrets
|
import secrets
|
||||||
@ -6,7 +5,7 @@ from asyncio import wait_for
|
|||||||
|
|
||||||
from onionrblocks import Block
|
from onionrblocks import Block
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
from ..dandelion import StemAcceptResult
|
from ..dandelion import StemAcceptResult
|
||||||
from ..constants import BLOCK_ID_SIZE, BLOCK_SIZE_LEN, BLOCK_MAX_SIZE
|
from ..constants import BLOCK_ID_SIZE, BLOCK_SIZE_LEN, BLOCK_MAX_SIZE
|
||||||
from ..constants import MAX_INBOUND_DANDELION_EDGE, MAX_STEM_BLOCKS_PER_STREAM
|
from ..constants import MAX_INBOUND_DANDELION_EDGE, MAX_STEM_BLOCKS_PER_STREAM
|
||||||
@ -36,13 +35,13 @@ async def accept_stem_blocks(
|
|||||||
|
|
||||||
for _ in range(MAX_STEM_BLOCKS_PER_STREAM):
|
for _ in range(MAX_STEM_BLOCKS_PER_STREAM):
|
||||||
read_routine = reader.readexactly(BLOCK_ID_SIZE)
|
read_routine = reader.readexactly(BLOCK_ID_SIZE)
|
||||||
#logging.debug(f"Reading block id in stem server")
|
logger.debug(f"Reading block id in stem server", terminal=True)
|
||||||
block_id = await wait_for(read_routine, base_wait_timeout)
|
block_id = await wait_for(read_routine, base_wait_timeout)
|
||||||
block_id = block_id.decode('utf-8')
|
block_id = block_id.decode('utf-8')
|
||||||
if not block_id:
|
if not block_id:
|
||||||
break
|
break
|
||||||
|
|
||||||
#logging.debug(f"Reading block size in stem server")
|
logger.debug(f"Reading block size in stem server", terminal=True)
|
||||||
block_size = (await wait_for(
|
block_size = (await wait_for(
|
||||||
reader.readexactly(BLOCK_SIZE_LEN),
|
reader.readexactly(BLOCK_SIZE_LEN),
|
||||||
base_wait_timeout)).decode('utf-8')
|
base_wait_timeout)).decode('utf-8')
|
||||||
@ -55,25 +54,17 @@ async def accept_stem_blocks(
|
|||||||
if block_size > BLOCK_MAX_SIZE:
|
if block_size > BLOCK_MAX_SIZE:
|
||||||
raise ValueError("Max block size")
|
raise ValueError("Max block size")
|
||||||
|
|
||||||
#logging.debug(f"Reading block of size {block_size} in stem server")
|
logger.debug(f"Reading block of size {block_size} in stem server", terminal=True)
|
||||||
|
|
||||||
raw_block: bytes = await wait_for(
|
raw_block: bytes = await wait_for(
|
||||||
reader.readexactly(block_size), base_wait_timeout * 6)
|
reader.readexactly(block_size), base_wait_timeout * 6)
|
||||||
if not raw_block:
|
if not raw_block:
|
||||||
break
|
break
|
||||||
|
|
||||||
try:
|
logger.debug("Got a stem block, put into queue", terminal=True)
|
||||||
bl = Block(block_id, raw_block, auto_verify=True)
|
block_queue_to_use.put(
|
||||||
except Exception as e:
|
Block(block_id, raw_block, auto_verify=True)
|
||||||
logging.warn(
|
)
|
||||||
f"Error in received stem block {block_id} {str(e)}")
|
|
||||||
logging.debug(traceback.format_exc())
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
logging.debug("Got a stem block, put into queue")
|
|
||||||
|
|
||||||
block_queue_to_use.put(bl)
|
|
||||||
|
|
||||||
# Regardless of stem phase, we add to queue
|
# Regardless of stem phase, we add to queue
|
||||||
# Client will decide if they are to be stemmed
|
# Client will decide if they are to be stemmed
|
||||||
|
@ -19,7 +19,7 @@ if TYPE_CHECKING:
|
|||||||
from ..constants import BLOCK_MAX_SIZE, BLOCK_SIZE_LEN
|
from ..constants import BLOCK_MAX_SIZE, BLOCK_SIZE_LEN
|
||||||
from ..constants import BLOCK_STREAM_OFFSET_DIGITS
|
from ..constants import BLOCK_STREAM_OFFSET_DIGITS
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
import blockdb
|
import blockdb
|
||||||
from blockdb import get_blocks_after_timestamp, block_storage_observers
|
from blockdb import get_blocks_after_timestamp, block_storage_observers
|
||||||
"""
|
"""
|
||||||
@ -103,5 +103,5 @@ async def diffuse_blocks(reader: 'StreamReader', writer: 'StreamWriter'):
|
|||||||
except ConnectionResetError:
|
except ConnectionResetError:
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warn(traceback.format_exc())
|
logger.warn(traceback.format_exc(), terminal=True)
|
||||||
|
|
||||||
|
81
src/keymanager.py
Executable file
81
src/keymanager.py
Executable file
@ -0,0 +1,81 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
Load, save, and delete the user's public key pairs (does not handle peer keys)
|
||||||
|
"""
|
||||||
|
from onionrutils import bytesconverter
|
||||||
|
from onionrcrypto import generate
|
||||||
|
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/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class KeyManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.keyFile = filepaths.keys_file
|
||||||
|
|
||||||
|
def addKey(self, pubKey=None, privKey=None):
|
||||||
|
"""Add a new key pair.
|
||||||
|
|
||||||
|
either specified or None to generate a new pair automatically
|
||||||
|
"""
|
||||||
|
if type(pubKey) is type(None) and type(privKey) is type(None):
|
||||||
|
pubKey, privKey = generate.generate_pub_key()
|
||||||
|
pubKey = bytesconverter.bytes_to_str(pubKey)
|
||||||
|
privKey = bytesconverter.bytes_to_str(privKey)
|
||||||
|
try:
|
||||||
|
if pubKey in self.getPubkeyList():
|
||||||
|
raise ValueError('Pubkey already in list: %s' % (pubKey,))
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
with open(self.keyFile, "a") as keyFile:
|
||||||
|
keyFile.write(pubKey + ',' + privKey + '\n')
|
||||||
|
return (pubKey, privKey)
|
||||||
|
|
||||||
|
def removeKey(self, pubKey):
|
||||||
|
"""Remove a key pair by pubkey"""
|
||||||
|
keyList = self.getPubkeyList()
|
||||||
|
keyData = ''
|
||||||
|
try:
|
||||||
|
keyList.remove(pubKey)
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
keyData = ','.join(keyList)
|
||||||
|
with open(self.keyFile, "w") as keyFile:
|
||||||
|
keyFile.write(keyData)
|
||||||
|
|
||||||
|
def getPubkeyList(self):
|
||||||
|
"""Return a list of the user's keys"""
|
||||||
|
keyList = []
|
||||||
|
try:
|
||||||
|
with open(self.keyFile, "r") as keyFile:
|
||||||
|
keyData = keyFile.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
keyData = ''
|
||||||
|
keyData = keyData.split('\n')
|
||||||
|
for pair in keyData:
|
||||||
|
if len(pair) > 0:
|
||||||
|
keyList.append(pair.split(',')[0])
|
||||||
|
return keyList
|
||||||
|
|
||||||
|
def getPrivkey(self, pubKey):
|
||||||
|
privKey = None
|
||||||
|
with open(self.keyFile, "r") as keyFile:
|
||||||
|
keyData = keyFile.read()
|
||||||
|
for pair in keyData.split('\n'):
|
||||||
|
if pubKey in pair or pubKey.replace('=', '') in pair:
|
||||||
|
privKey = pair.split(',')[1]
|
||||||
|
return privKey
|
@ -1,92 +1,71 @@
|
|||||||
"""
|
'''
|
||||||
Onionr - Private P2P Communication
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
We use built in logging but with a custom formatter for colors and such
|
This file handles all operations involving logging
|
||||||
"""
|
'''
|
||||||
import logging
|
'''
|
||||||
import sys
|
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.
|
||||||
|
|
||||||
from filepaths import log_file
|
This program is distributed in the hope that it will be useful,
|
||||||
"""
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
This program is free software: you can redistribute it and/or modify
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
it under the terms of the GNU General Public License as published by
|
GNU General Public License for more details.
|
||||||
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,
|
You should have received a copy of the GNU General Public License
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
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
|
import sys, traceback
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# credit: https://stackoverflow.com/a/384076
|
from . import colors, readline, log, raw, confirm, colors, settings
|
||||||
# license: https://creativecommons.org/licenses/by-sa/4.0/
|
colors = colors.Colors
|
||||||
class ConsoleFormatter(logging.Formatter):
|
readline = readline.readline
|
||||||
|
log = log.log
|
||||||
|
raw = raw.raw
|
||||||
|
confirm = confirm.confirm
|
||||||
|
|
||||||
grey = "\x1b[38;20m"
|
# debug: when there is info that could be useful for debugging purposes only
|
||||||
green = "\x1b[38;5;82m"
|
def debug(data: str, error = None, timestamp = True, prompt = True, terminal = False, level = settings.LEVEL_DEBUG):
|
||||||
yellow = "\x1b[33;20m"
|
if settings.get_level() <= level:
|
||||||
red = "\x1b[31;20m"
|
log('/', data, timestamp = timestamp, prompt = prompt, terminal = terminal)
|
||||||
bold_red = "\x1b[31;1m"
|
if not error is None:
|
||||||
reset = "\x1b[0m"
|
debug('Error: ' + str(error) + parse_error())
|
||||||
format_default = "%(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
|
|
||||||
format_info = "%(message)s - (%(filename)s:%(lineno)d)"
|
|
||||||
|
|
||||||
FORMATS = {
|
# info: when there is something to notify the user of, such as the success of a process
|
||||||
logging.DEBUG: grey + format_default + reset,
|
def info(data: str, timestamp = False, prompt = True, terminal = False, level = settings.LEVEL_INFO):
|
||||||
logging.INFO: green + format_info + reset,
|
if settings.get_level() <= level:
|
||||||
logging.WARNING: yellow + format_default + reset,
|
log('+', data, colors.fg.green, timestamp = timestamp, prompt = prompt, terminal = terminal)
|
||||||
logging.ERROR: red + format_default + reset,
|
|
||||||
logging.CRITICAL: bold_red + format_default + reset
|
|
||||||
}
|
|
||||||
|
|
||||||
def format(self, record):
|
# warn: when there is a potential for something bad to happen
|
||||||
log_fmt = self.FORMATS.get(record.levelno)
|
def warn(data: str, error = None, timestamp = True, prompt = True, terminal = False, level = settings.LEVEL_WARN):
|
||||||
formatter = logging.Formatter(log_fmt)
|
if not error is None:
|
||||||
return formatter.format(record)
|
debug('Error: ' + str(error) + parse_error())
|
||||||
|
if settings.get_level() <= level:
|
||||||
|
log('!', data, colors.fg.orange, timestamp = timestamp, prompt = prompt, terminal = terminal)
|
||||||
|
|
||||||
class FileFormatter(logging.Formatter):
|
# error: when only one function, module, or process of the program encountered a problem and must stop
|
||||||
|
def error(data: str, error = None, timestamp = True, prompt = True, terminal = False, level = settings.LEVEL_ERROR):
|
||||||
|
if settings.get_level() <= level:
|
||||||
|
log('-', data, colors.fg.red, timestamp = timestamp, fd = sys.stderr, prompt = prompt, terminal = terminal)
|
||||||
|
if not error is None:
|
||||||
|
debug('Error: ' + str(error) + parse_error())
|
||||||
|
|
||||||
|
# fatal: when the something so bad has happened that the program must stop
|
||||||
|
def fatal(data: str, error = None, timestamp=True, prompt = True, terminal = False, level = settings.LEVEL_FATAL):
|
||||||
|
if not error is None:
|
||||||
|
debug('Error: ' + str(error) + parse_error(), terminal = terminal)
|
||||||
|
if settings.get_level() <= level:
|
||||||
|
log('#', data, colors.bg.red + colors.fg.green + colors.bold, timestamp = timestamp, fd = sys.stderr, prompt = prompt, terminal = terminal)
|
||||||
|
|
||||||
format_default = "%(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
|
# returns a formatted error message
|
||||||
format_info = "%(message)s - (%(filename)s:%(lineno)d)"
|
def parse_error():
|
||||||
|
details = traceback.extract_tb(sys.exc_info()[2])
|
||||||
|
output = ''
|
||||||
|
|
||||||
FORMATS = {
|
for line in details:
|
||||||
logging.DEBUG: format_default,
|
output += '\n ... module %s in %s:%i' % (line[2], line[0], line[1])
|
||||||
logging.INFO: format_info,
|
|
||||||
logging.WARNING: format_default,
|
|
||||||
logging.ERROR: format_default,
|
|
||||||
logging.CRITICAL: format_default
|
|
||||||
}
|
|
||||||
|
|
||||||
def format(self, record):
|
return output
|
||||||
log_fmt = self.FORMATS.get(record.levelno)
|
|
||||||
formatter = logging.Formatter(log_fmt)
|
|
||||||
return formatter.format(record)
|
|
||||||
|
|
||||||
|
|
||||||
#logging.basicConfig(level=logging.ERROR, format='%(message)s ')
|
|
||||||
log = logging.getLogger('onionr')
|
|
||||||
log.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
ch = logging.StreamHandler(sys.stdout)
|
|
||||||
ch.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
ch.setFormatter(ConsoleFormatter())
|
|
||||||
|
|
||||||
|
|
||||||
def enable_file_logging():
|
|
||||||
fh = logging.FileHandler(log_file)
|
|
||||||
fh.setLevel(logging.INFO)
|
|
||||||
fh.setFormatter(FileFormatter())
|
|
||||||
log.addHandler(fh)
|
|
||||||
|
|
||||||
def disable_console_logging():
|
|
||||||
log.removeHandler(ch)
|
|
||||||
|
|
||||||
def enable_console_logging():
|
|
||||||
log.addHandler(ch)
|
|
||||||
enable_console_logging()
|
|
||||||
|
60
src/logger/colors.py
Normal file
60
src/logger/colors.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
'''
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
class to access ANSI control codes
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
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 re
|
||||||
|
class Colors:
|
||||||
|
'''
|
||||||
|
This class allows you to set the color if ANSI codes are supported
|
||||||
|
'''
|
||||||
|
reset='\033[0m'
|
||||||
|
bold='\033[01m'
|
||||||
|
disable='\033[02m'
|
||||||
|
underline='\033[04m'
|
||||||
|
reverse='\033[07m'
|
||||||
|
strikethrough='\033[09m'
|
||||||
|
invisible='\033[08m'
|
||||||
|
italics='\033[3m'
|
||||||
|
class fg:
|
||||||
|
black='\033[30m'
|
||||||
|
red='\033[31m'
|
||||||
|
green='\033[32m'
|
||||||
|
orange='\033[33m'
|
||||||
|
blue='\033[34m'
|
||||||
|
purple='\033[35m'
|
||||||
|
cyan='\033[36m'
|
||||||
|
lightgrey='\033[37m'
|
||||||
|
darkgrey='\033[90m'
|
||||||
|
lightred='\033[91m'
|
||||||
|
lightgreen='\033[92m'
|
||||||
|
yellow='\033[93m'
|
||||||
|
lightblue='\033[94m'
|
||||||
|
pink='\033[95m'
|
||||||
|
lightcyan='\033[96m'
|
||||||
|
class bg:
|
||||||
|
black='\033[40m'
|
||||||
|
red='\033[41m'
|
||||||
|
green='\033[42m'
|
||||||
|
orange='\033[43m'
|
||||||
|
blue='\033[44m'
|
||||||
|
purple='\033[45m'
|
||||||
|
cyan='\033[46m'
|
||||||
|
lightgrey='\033[47m'
|
||||||
|
@staticmethod
|
||||||
|
def filter(data):
|
||||||
|
return re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]').sub('', str(data))
|
54
src/logger/confirm.py
Normal file
54
src/logger/confirm.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
'''
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
confirm y/n cli prompt
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
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 sys
|
||||||
|
from . import colors, settings
|
||||||
|
colors = colors.Colors
|
||||||
|
def confirm(default = 'y', message = 'Are you sure %s? '):
|
||||||
|
'''
|
||||||
|
Displays an "Are you sure" message, returns True for Y and False for N
|
||||||
|
message: The confirmation message, use %s for (y/n)
|
||||||
|
default: which to prefer-- y or n
|
||||||
|
'''
|
||||||
|
|
||||||
|
color = colors.fg.green + colors.bold
|
||||||
|
|
||||||
|
default = default.lower()
|
||||||
|
confirm = colors.bold
|
||||||
|
if default.startswith('y'):
|
||||||
|
confirm += '(Y/n)'
|
||||||
|
else:
|
||||||
|
confirm += '(y/N)'
|
||||||
|
confirm += colors.reset + color
|
||||||
|
|
||||||
|
output = colors.reset + str(color) + '... ' + colors.reset + str(message) + colors.reset
|
||||||
|
|
||||||
|
if not get_settings() & settings.USE_ANSI:
|
||||||
|
output = colors.filter(output)
|
||||||
|
|
||||||
|
sys.stdout.write(output.replace('%s', confirm))
|
||||||
|
|
||||||
|
inp = input().lower()
|
||||||
|
|
||||||
|
if 'y' in inp:
|
||||||
|
return True
|
||||||
|
if 'n' in inp:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return default == 'y'
|
38
src/logger/log.py
Normal file
38
src/logger/log.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
'''
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
god log function
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
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 sys, time
|
||||||
|
from . import colors, raw, settings
|
||||||
|
colors = colors.Colors
|
||||||
|
def log(prefix, data, color = '', timestamp=True, fd = sys.stdout, prompt = True, terminal = False):
|
||||||
|
'''
|
||||||
|
Logs the data
|
||||||
|
prefix : The prefix to the output
|
||||||
|
data : The actual data to output
|
||||||
|
color : The color to output before the data
|
||||||
|
'''
|
||||||
|
curTime = ''
|
||||||
|
if timestamp:
|
||||||
|
curTime = time.strftime("%m-%d %H:%M:%S") + ' '
|
||||||
|
|
||||||
|
output = colors.reset + str(color) + ('[' + colors.bold + str(prefix) + colors.reset + str(color) + '] ' if prompt is True else '') + curTime + str(data) + colors.reset
|
||||||
|
if not settings.get_settings() & settings.USE_ANSI:
|
||||||
|
output = colors.filter(output)
|
||||||
|
|
||||||
|
raw.raw(output, fd = fd, terminal = terminal)
|
54
src/logger/raw.py
Normal file
54
src/logger/raw.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
Output raw data to file or terminal
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from . import settings, colors
|
||||||
|
"""
|
||||||
|
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/>.
|
||||||
|
"""
|
||||||
|
colors = colors.Colors
|
||||||
|
|
||||||
|
|
||||||
|
def raw(data, fd = sys.stdout, terminal = False):
|
||||||
|
"""
|
||||||
|
Outputs raw data to console without formatting
|
||||||
|
"""
|
||||||
|
|
||||||
|
if terminal and (settings.get_settings() & settings.OUTPUT_TO_CONSOLE):
|
||||||
|
try:
|
||||||
|
ts = fd.write('%s\n' % data)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
if settings.get_settings() & settings.OUTPUT_TO_FILE:
|
||||||
|
fdata = ''
|
||||||
|
try:
|
||||||
|
for _ in range(5):
|
||||||
|
try:
|
||||||
|
with open(settings._outputfile, 'r') as file:
|
||||||
|
fdata = file.read()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
fdata = fdata + '\n' + data
|
||||||
|
fdata = fdata.split('\n')
|
||||||
|
if len(fdata) >= settings.MAX_LOG_FILE_LINES:
|
||||||
|
fdata.pop(0)
|
||||||
|
fdata = '\n'.join(fdata)
|
||||||
|
with open(settings._outputfile, 'w') as file:
|
||||||
|
file.write(fdata)
|
37
src/logger/readline.py
Normal file
37
src/logger/readline.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
'''
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
get a line of input from stdin
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
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 sys
|
||||||
|
from . import colors, settings
|
||||||
|
colors = colors.Colors
|
||||||
|
def readline(message = ''):
|
||||||
|
'''
|
||||||
|
Takes in input from the console, not stored in logs
|
||||||
|
message: The message to display before taking input
|
||||||
|
'''
|
||||||
|
|
||||||
|
color = colors.fg.green + colors.bold
|
||||||
|
output = colors.reset + str(color) + '... ' + colors.reset + str(message) + colors.reset
|
||||||
|
|
||||||
|
if not settings.get_settings() & settings.USE_ANSI:
|
||||||
|
output = colors.filter(output)
|
||||||
|
|
||||||
|
sys.stdout.write(output)
|
||||||
|
|
||||||
|
return input()
|
89
src/logger/settings.py
Normal file
89
src/logger/settings.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
'''
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
logger settings
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
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 os
|
||||||
|
from utils import identifyhome
|
||||||
|
import filepaths
|
||||||
|
|
||||||
|
data_home = os.environ.get('ONIONR_LOG_DIR', identifyhome.identify_home())
|
||||||
|
# Use the bitwise operators to merge these settings
|
||||||
|
USE_ANSI = 0b100
|
||||||
|
if os.name == 'nt':
|
||||||
|
USE_ANSI = 0b000
|
||||||
|
OUTPUT_TO_CONSOLE = 0b010
|
||||||
|
OUTPUT_TO_FILE = 0b001
|
||||||
|
|
||||||
|
LEVEL_DEBUG = 1
|
||||||
|
LEVEL_INFO = 2
|
||||||
|
LEVEL_WARN = 3
|
||||||
|
LEVEL_ERROR = 4
|
||||||
|
LEVEL_FATAL = 5
|
||||||
|
LEVEL_IMPORTANT = 6
|
||||||
|
|
||||||
|
MAX_LOG_FILE_LINES = 10000
|
||||||
|
|
||||||
|
_type = OUTPUT_TO_CONSOLE | USE_ANSI # the default settings for logging
|
||||||
|
_level = LEVEL_DEBUG # the lowest level to log
|
||||||
|
# the file to log to
|
||||||
|
_outputfile = filepaths.log_file
|
||||||
|
|
||||||
|
def set_settings(type):
|
||||||
|
'''
|
||||||
|
Set the settings for the logger using bitwise operators
|
||||||
|
'''
|
||||||
|
|
||||||
|
global _type
|
||||||
|
_type = type
|
||||||
|
|
||||||
|
def get_settings():
|
||||||
|
'''
|
||||||
|
Get settings from the logger
|
||||||
|
'''
|
||||||
|
|
||||||
|
return _type
|
||||||
|
|
||||||
|
def set_level(level):
|
||||||
|
'''
|
||||||
|
Set the lowest log level to output
|
||||||
|
'''
|
||||||
|
|
||||||
|
global _level
|
||||||
|
_level = level
|
||||||
|
|
||||||
|
def get_level()->int:
|
||||||
|
'''
|
||||||
|
Get the lowest log level currently being outputted
|
||||||
|
'''
|
||||||
|
|
||||||
|
return _level
|
||||||
|
|
||||||
|
def set_file(outputfile):
|
||||||
|
'''
|
||||||
|
Set the file to output to, if enabled
|
||||||
|
'''
|
||||||
|
|
||||||
|
global _outputfile
|
||||||
|
_outputfile = outputfile
|
||||||
|
|
||||||
|
def get_file():
|
||||||
|
'''
|
||||||
|
Get the file to output to
|
||||||
|
'''
|
||||||
|
|
||||||
|
return _outputfile
|
@ -8,19 +8,20 @@ import sys
|
|||||||
import platform
|
import platform
|
||||||
import signal
|
import signal
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from logger import log as logging
|
|
||||||
from logger import enable_file_logging
|
|
||||||
|
|
||||||
import filenuke
|
import filenuke
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
import config
|
import config
|
||||||
|
|
||||||
|
import logger
|
||||||
from onionrplugins import onionrevents as events
|
from onionrplugins import onionrevents as events
|
||||||
|
|
||||||
from utils import identifyhome
|
from utils import identifyhome
|
||||||
import filepaths
|
import filepaths
|
||||||
import onionrvalues
|
import onionrvalues
|
||||||
|
from onionrutils import cleanup
|
||||||
|
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
|
||||||
from .. import version
|
from .. import version
|
||||||
@ -43,39 +44,25 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _safe_remove(path):
|
|
||||||
try:
|
|
||||||
os.remove(path)
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def delete_run_files():
|
|
||||||
"""Delete run files, do not error if not found.
|
|
||||||
|
|
||||||
Test: test_cleanup.py
|
|
||||||
"""
|
|
||||||
_safe_remove(filepaths.lock_file)
|
|
||||||
_safe_remove(filepaths.gossip_server_socket_file)
|
|
||||||
_safe_remove(filepaths.pid_file)
|
|
||||||
|
|
||||||
def _show_info_messages():
|
def _show_info_messages():
|
||||||
version.version(verbosity=5, function=logging.info)
|
version.version(verbosity=5, function=logger.info)
|
||||||
logging.debug('Python version %s' % platform.python_version())
|
logger.debug('Python version %s' % platform.python_version())
|
||||||
|
|
||||||
if onionrvalues.DEVELOPMENT_MODE:
|
if onionrvalues.DEVELOPMENT_MODE:
|
||||||
logging.warn('Development mode enabled')
|
logger.warn('Development mode enabled', timestamp=False, terminal=True)
|
||||||
|
|
||||||
|
logger.info('Using public key: %s' %
|
||||||
|
(logger.colors.underline +
|
||||||
|
getourkeypair.get_keypair()[0][:52]))
|
||||||
|
|
||||||
|
|
||||||
def daemon():
|
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."""
|
||||||
|
|
||||||
if config.get('log.file.output', False):
|
|
||||||
enable_file_logging()
|
|
||||||
|
|
||||||
def _handle_sig_term(signum, frame):
|
def _handle_sig_term(signum, frame):
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
with open(filepaths.pid_file, 'w') as f:
|
with open(filepaths.pid_file, 'w') as f:
|
||||||
f.write(str(os.getpid()))
|
f.write(str(os.getpid()))
|
||||||
|
|
||||||
@ -87,30 +74,26 @@ def daemon():
|
|||||||
security_level = config.get('general.security_level', 1)
|
security_level = config.get('general.security_level', 1)
|
||||||
|
|
||||||
_show_info_messages()
|
_show_info_messages()
|
||||||
logging.info(
|
logger.info(
|
||||||
f"Onionr daemon is running under pid {os.getpid()}")
|
f"Onionr daemon is running under pid {os.getpid()}", terminal=True)
|
||||||
events.event('init', threaded=False)
|
events.event('init', threaded=False)
|
||||||
events.event('afterinit', threaded=False)
|
|
||||||
events.event('daemon_start')
|
events.event('daemon_start')
|
||||||
|
|
||||||
add_onionr_thread(
|
add_onionr_thread(
|
||||||
clean_block_database, 60, 'clean_block_database', initial_sleep=0)
|
clean_block_database, 60, 'clean_block_database', initial_sleep=0)
|
||||||
|
|
||||||
Thread(
|
Thread(
|
||||||
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:
|
||||||
while True:
|
while True:
|
||||||
# Mainly for things like repls
|
|
||||||
events.event('primary_loop', threaded=False)
|
|
||||||
sleep(60)
|
sleep(60)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
cleanup.delete_run_files()
|
||||||
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())
|
||||||
|
|
||||||
@ -121,25 +104,33 @@ def start(override: bool = False):
|
|||||||
Error exit if there is and its not overridden
|
Error exit if there is and its not overridden
|
||||||
"""
|
"""
|
||||||
if os.path.exists(filepaths.lock_file) and not override:
|
if os.path.exists(filepaths.lock_file) and not override:
|
||||||
|
if os.path.exists(filepaths.restarting_indicator):
|
||||||
|
try:
|
||||||
|
os.remove(filepaths.restarting_indicator)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return
|
||||||
with open(filepaths.lock_file, 'r') as lock_file:
|
with open(filepaths.lock_file, 'r') as lock_file:
|
||||||
try:
|
try:
|
||||||
proc = psutil.Process(int(lock_file.read())).name()
|
proc = psutil.Process(int(lock_file.read())).name()
|
||||||
except psutil.NoSuchProcess:
|
except psutil.NoSuchProcess:
|
||||||
proc = ""
|
proc = ""
|
||||||
if not proc.startswith("python"):
|
if not proc.startswith("python"):
|
||||||
logging.warn(
|
logger.warn(
|
||||||
f"Detected stale run file, deleting {filepaths.lock_file}")
|
f"Detected stale run file, deleting {filepaths.lock_file}",
|
||||||
|
terminal=True)
|
||||||
try:
|
try:
|
||||||
os.remove(filepaths.lock_file)
|
os.remove(filepaths.lock_file)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
start(override=True)
|
start(override=True)
|
||||||
return
|
return
|
||||||
logging.error('Cannot start. Daemon is already running,'
|
logger.fatal('Cannot start. Daemon is already running,'
|
||||||
+ ' or it did not exit cleanly.\n'
|
+ ' or it did not exit cleanly.\n'
|
||||||
+ ' (if you are sure that there is not a daemon running,'
|
+ ' (if you are sure that there is not a daemon running,'
|
||||||
+ f' delete {filepaths.lock_file} & try again).',
|
+ f' delete {filepaths.lock_file} & try again).',
|
||||||
)
|
terminal=True)
|
||||||
else:
|
else:
|
||||||
if not onionrvalues.DEVELOPMENT_MODE:
|
if not onionrvalues.DEVELOPMENT_MODE:
|
||||||
lock_file = open(filepaths.lock_file, 'w')
|
lock_file = open(filepaths.lock_file, 'w')
|
||||||
|
24
src/onionrcommands/daemonlaunch/geturl.py
Normal file
24
src/onionrcommands/daemonlaunch/geturl.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
Open the web interface properly into a web browser, and return it
|
||||||
|
"""
|
||||||
|
import logger
|
||||||
|
from onionrutils import getclientapiserver
|
||||||
|
|
||||||
|
|
||||||
|
def get_url(config) -> str:
|
||||||
|
"""Build UI URL string and return it."""
|
||||||
|
onboarding = ""
|
||||||
|
if not config.get('onboarding.done', False):
|
||||||
|
onboarding = "onboarding/"
|
||||||
|
try:
|
||||||
|
url = getclientapiserver.get_client_API_server()
|
||||||
|
except FileNotFoundError:
|
||||||
|
url = ""
|
||||||
|
logger.error(
|
||||||
|
'Onionr seems to not be running (could not get api host)',
|
||||||
|
terminal=True)
|
||||||
|
else:
|
||||||
|
url = 'http://%s/%s#%s' % (url, onboarding, config.get('client.webpassword'))
|
||||||
|
logger.info('Onionr web interface URL: ' + url, terminal=True)
|
||||||
|
return url
|
@ -6,7 +6,7 @@ import os
|
|||||||
from signal import SIGTERM
|
from signal import SIGTERM
|
||||||
|
|
||||||
from filepaths import pid_file
|
from filepaths import pid_file
|
||||||
from logger import log as logging
|
import logger
|
||||||
"""
|
"""
|
||||||
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,8 +29,9 @@ def kill_daemon():
|
|||||||
with open(pid_file, 'r') as pid:
|
with open(pid_file, 'r') as pid:
|
||||||
os.kill(int(pid.read()), SIGTERM)
|
os.kill(int(pid.read()), SIGTERM)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logging.error("Daemon not running/pid file missing")
|
logger.error("Daemon not running/pid file missing")
|
||||||
logging.warn('Stopping the running daemon, if one exists...')
|
logger.warn('Stopping the running daemon, if one exists...', timestamp=False,
|
||||||
|
terminal=True)
|
||||||
|
|
||||||
|
|
||||||
kill_daemon.onionr_help = "Gracefully stops the " # type: ignore
|
kill_daemon.onionr_help = "Gracefully stops the " # type: ignore
|
||||||
|
@ -5,12 +5,11 @@ Show nice logo
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import config
|
import config
|
||||||
|
import logger
|
||||||
|
|
||||||
from .quotes import QUOTE
|
from .quotes import QUOTE
|
||||||
from utils.boxprint import bordered
|
from utils.boxprint import bordered
|
||||||
|
from utils import logoheader
|
||||||
from utils import readstatic
|
|
||||||
import onionrvalues
|
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -26,34 +25,28 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def header():
|
|
||||||
if onionrvalues.DEVELOPMENT_MODE:
|
|
||||||
return
|
|
||||||
|
|
||||||
pink_ansi = '\033[95m'
|
|
||||||
green_ansi = '\033[92m'
|
|
||||||
reset_ansi = '\x1b[0m'
|
|
||||||
|
|
||||||
logo = readstatic.read_static('header.txt', ret_bin=False)
|
|
||||||
logo = logo.replace('P', pink_ansi).replace('G', green_ansi).replace('W', reset_ansi)
|
|
||||||
print(reset_ansi + logo)
|
|
||||||
|
|
||||||
def show_logo():
|
def show_logo():
|
||||||
|
logger.raw('', terminal=True)
|
||||||
try:
|
try:
|
||||||
terminal_size = os.get_terminal_size().columns
|
terminal_size = os.get_terminal_size().columns
|
||||||
except OSError: # Generally thrown if not in terminal
|
except OSError: # Generally thrown if not in terminal
|
||||||
terminal_size = 120
|
terminal_size = 120
|
||||||
# print nice header thing :)
|
# print nice header thing :)
|
||||||
if config.get('general.display_header', True):
|
if config.get('general.display_header', True):
|
||||||
header()
|
logoheader.header("")
|
||||||
if terminal_size >= 120:
|
if terminal_size >= 120:
|
||||||
if QUOTE[1]: # If there is an author to show for the quote
|
if QUOTE[1]: # If there is an author to show for the quote
|
||||||
print("\u001b[33m\033[F" + bordered(QUOTE[0] + '\n -' + QUOTE[1]))
|
logger.info(
|
||||||
|
"\u001b[33m\033[F" + bordered(QUOTE[0] + '\n -' + QUOTE[1]),
|
||||||
|
terminal=True)
|
||||||
else:
|
else:
|
||||||
print("\u001b[33m\033[F" + bordered(QUOTE[0]))
|
logger.info(
|
||||||
|
"\u001b[33m\033[F" + bordered(QUOTE[0]), terminal=True)
|
||||||
else:
|
else:
|
||||||
if QUOTE[1]:
|
if QUOTE[1]:
|
||||||
print("\u001b[33m\033[F" + QUOTE[0] + '\n -' + QUOTE[1])
|
logger.info("\u001b[33m\033[F" + QUOTE[0] + '\n -' + QUOTE[1],
|
||||||
|
terminal=True)
|
||||||
else:
|
else:
|
||||||
print("\u001b[33m\033[F" + QUOTE[0])
|
logger.info("\u001b[33m\033[F" + QUOTE[0], terminal=True)
|
||||||
|
|
||||||
|
146
src/onionrcommands/onionrstatistics.py
Executable file
146
src/onionrcommands/onionrstatistics.py
Executable file
@ -0,0 +1,146 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
This module defines commands to show stats/details about the local node
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import logger
|
||||||
|
from onionrutils import mnemonickeys
|
||||||
|
from utils import sizeutils, getconsolewidth, identifyhome
|
||||||
|
from coredb import keydb
|
||||||
|
import onionrcrypto
|
||||||
|
import config
|
||||||
|
import onionrvalues
|
||||||
|
from filepaths import lock_file
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
"""
|
||||||
|
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 _is_running():
|
||||||
|
script = onionrvalues.SCRIPT_NAME
|
||||||
|
if os.path.isfile(lock_file):
|
||||||
|
for process in psutil.process_iter():
|
||||||
|
if process.name() == script:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def show_stats():
|
||||||
|
"""Print/log statistic info about our Onionr install."""
|
||||||
|
try:
|
||||||
|
# define stats messages here
|
||||||
|
home = identifyhome.identify_home()
|
||||||
|
|
||||||
|
|
||||||
|
messages = {
|
||||||
|
# info about local client
|
||||||
|
|
||||||
|
# This line is inaccurate if dev mode is enabled
|
||||||
|
'Onionr Daemon Status':
|
||||||
|
((logger.colors.fg.green + 'Online') \
|
||||||
|
if _is_running() \
|
||||||
|
else logger.colors.fg.red + 'Offline'),
|
||||||
|
|
||||||
|
# file and folder size stats
|
||||||
|
'div1': True, # this creates a solid line across the screen, a div
|
||||||
|
'Total Block Size':
|
||||||
|
sizeutils.human_size(sizeutils.size(home + 'blocks.db')),
|
||||||
|
'Total Plugin Size':
|
||||||
|
sizeutils.human_size(sizeutils.size(home + 'plugins/')),
|
||||||
|
'Log File Size':
|
||||||
|
sizeutils.human_size(sizeutils.size(home + 'output.log')),
|
||||||
|
|
||||||
|
# count stats
|
||||||
|
'div2': True,
|
||||||
|
'Enabled Plugins':
|
||||||
|
str(len(config.get('plugins.enabled', list()))) + ' / ' +
|
||||||
|
str(len(os.listdir(home + 'plugins/')))
|
||||||
|
}
|
||||||
|
|
||||||
|
# color configuration
|
||||||
|
colors = {
|
||||||
|
'title': logger.colors.bold,
|
||||||
|
'key': logger.colors.fg.lightgreen,
|
||||||
|
'val': logger.colors.fg.green,
|
||||||
|
'border': logger.colors.fg.lightblue,
|
||||||
|
|
||||||
|
'reset': logger.colors.reset
|
||||||
|
}
|
||||||
|
|
||||||
|
# pre-processing
|
||||||
|
maxlength = 0
|
||||||
|
width = getconsolewidth.get_console_width()
|
||||||
|
for key, val in messages.items():
|
||||||
|
if not (type(val) is bool and val is True):
|
||||||
|
maxlength = max(len(key), maxlength)
|
||||||
|
prewidth = maxlength + len(' | ')
|
||||||
|
groupsize = width - prewidth - len('[+] ')
|
||||||
|
|
||||||
|
# generate stats table
|
||||||
|
logger.info(colors['title'] + 'Onionr v%s Statistics' %
|
||||||
|
onionrvalues.ONIONR_VERSION + colors['reset'],
|
||||||
|
terminal=True)
|
||||||
|
logger.info(colors['border'] + '-' * (maxlength + 1) +
|
||||||
|
'+' + colors['reset'], terminal=True)
|
||||||
|
for key, val in messages.items():
|
||||||
|
if not (type(val) is bool and val is True):
|
||||||
|
val = [str(val)[i:i + groupsize]
|
||||||
|
for i in range(0, len(str(val)), groupsize)]
|
||||||
|
|
||||||
|
logger.info(colors['key'] + str(key).rjust(maxlength) +
|
||||||
|
colors['reset'] + colors['border'] +
|
||||||
|
' | ' + colors['reset'] + colors['val'] +
|
||||||
|
str(val.pop(0)) + colors['reset'], terminal=True)
|
||||||
|
|
||||||
|
for value in val:
|
||||||
|
logger.info(' ' * maxlength + colors['border'] + ' | ' +
|
||||||
|
colors['reset'] + colors['val'] + str(
|
||||||
|
value) + colors['reset'], terminal=True)
|
||||||
|
else:
|
||||||
|
logger.info(colors['border'] + '-' * (maxlength +
|
||||||
|
1) + '+' +
|
||||||
|
colors['reset'], terminal=True)
|
||||||
|
logger.info(colors['border'] + '-' * (maxlength + 1) +
|
||||||
|
'+' + colors['reset'], terminal=True)
|
||||||
|
except Exception as e: # pylint: disable=W0703
|
||||||
|
logger.error('Failed to generate statistics table. ' +
|
||||||
|
str(e), error=e, timestamp=False, terminal=True)
|
||||||
|
|
||||||
|
|
||||||
|
def show_details():
|
||||||
|
"""Print out details.
|
||||||
|
|
||||||
|
node transport address(es)
|
||||||
|
active user ID
|
||||||
|
active user ID in mnemonic form
|
||||||
|
"""
|
||||||
|
details = {
|
||||||
|
'Data directory': identifyhome.identify_home(),
|
||||||
|
'Public Key': onionrcrypto.pub_key.replace('=', ''),
|
||||||
|
'Human-readable Public Key': mnemonickeys.get_human_readable_ID()
|
||||||
|
}
|
||||||
|
|
||||||
|
for detail in details:
|
||||||
|
logger.info('%s%s: \n%s%s\n' % (logger.colors.fg.lightgreen,
|
||||||
|
detail, logger.colors.fg.green,
|
||||||
|
details[detail]), terminal=True)
|
||||||
|
|
||||||
|
|
||||||
|
show_details.onionr_help = "Shows relevant information " # type: ignore
|
||||||
|
show_details.onionr_help += "for your Onionr install:"
|
||||||
|
|
||||||
|
show_stats.onionr_help = "Shows statistics for your Onionr " # type: ignore
|
||||||
|
show_stats.onionr_help += "node. Slow if Onionr is not running" # type: ignore
|
@ -11,7 +11,7 @@ try:
|
|||||||
except (KeyError, IndexError) as _:
|
except (KeyError, IndexError) as _:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
import onionrexceptions
|
import onionrexceptions
|
||||||
import onionrplugins
|
import onionrplugins
|
||||||
from onionrplugins import onionrpluginapi
|
from onionrplugins import onionrpluginapi
|
||||||
@ -48,6 +48,10 @@ def register_plugin_commands(cmd) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _show_term(msg: str):
|
||||||
|
logger.info(msg, terminal=True)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
"""Register commands and handles help command processing."""
|
"""Register commands and handles help command processing."""
|
||||||
def get_help_message(cmd: str,
|
def get_help_message(cmd: str,
|
||||||
@ -76,7 +80,7 @@ def register():
|
|||||||
try:
|
try:
|
||||||
cmd = sys.argv[1]
|
cmd = sys.argv[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
logging.info('Run with --help to see available commands')
|
logger.info('Run with --help to see available commands', terminal=True)
|
||||||
sys.exit(10)
|
sys.exit(10)
|
||||||
|
|
||||||
is_help_cmd = False
|
is_help_cmd = False
|
||||||
@ -98,28 +102,29 @@ def register():
|
|||||||
sys.argv[2]
|
sys.argv[2]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
for i in arguments.get_arguments():
|
for i in arguments.get_arguments():
|
||||||
logging.info('%s <%s>: %s' % (PROGRAM_NAME, '/'.join(i),
|
_show_term('%s <%s>: %s' % (PROGRAM_NAME, '/'.join(i),
|
||||||
get_help_message(i[0])))
|
get_help_message(i[0])))
|
||||||
for pl in onionrplugins.get_enabled_plugins():
|
for pl in onionrplugins.get_enabled_plugins():
|
||||||
pl = onionrplugins.get_plugin(pl)
|
pl = onionrplugins.get_plugin(pl)
|
||||||
if hasattr(pl, 'ONIONR_COMMANDS'):
|
if hasattr(pl, 'ONIONR_COMMANDS'):
|
||||||
print('')
|
print('')
|
||||||
try:
|
try:
|
||||||
logging.info('%s commands:' % (pl.plugin_name,))
|
_show_term('%s commands:' % (pl.plugin_name,))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logging.info('%s commands:' % (pl.__name__,))
|
_show_term('%s commands:' % (pl.__name__,))
|
||||||
for plugin_cmd in pl.ONIONR_COMMANDS:
|
for plugin_cmd in pl.ONIONR_COMMANDS:
|
||||||
logging.info('%s %s: %s' %
|
_show_term('%s %s: %s' %
|
||||||
(PROGRAM_NAME,
|
(PROGRAM_NAME,
|
||||||
plugin_cmd,
|
plugin_cmd,
|
||||||
get_help_message(plugin_cmd)),)
|
get_help_message(plugin_cmd)),)
|
||||||
print('')
|
print('')
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
logging.info('%s %s: %s' % (PROGRAM_NAME,
|
_show_term('%s %s: %s' % (PROGRAM_NAME,
|
||||||
sys.argv[2],
|
sys.argv[2],
|
||||||
get_help_message(sys.argv[2])))
|
get_help_message(sys.argv[2])))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logging.error('%s: command does not exist.' % [sys.argv[2]])
|
logger.error('%s: command does not exist.' % [sys.argv[2]],
|
||||||
|
terminal=True)
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
return
|
return
|
||||||
|
@ -4,7 +4,8 @@ Sets CLI arguments for Onionr
|
|||||||
"""
|
"""
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from .. import version, daemonlaunch
|
from .. import onionrstatistics, version, daemonlaunch
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
@ -32,10 +33,14 @@ def get_arguments() -> dict:
|
|||||||
dynamically modify them with plugins
|
dynamically modify them with plugins
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
('details', 'info'): onionrstatistics.show_details,
|
||||||
|
('stats', 'statistics'): onionrstatistics.show_stats,
|
||||||
('version',): version.version,
|
('version',): version.version,
|
||||||
('start', 'daemon'): daemonlaunch.start,
|
('start', 'daemon'): daemonlaunch.start,
|
||||||
('stop', 'kill'): daemonlaunch.kill_daemon,
|
('stop', 'kill'): daemonlaunch.kill_daemon,
|
||||||
('resetplugins', 'reset-plugins', 'updateplugins', 'update-plugins'): resetplugins.reset
|
('addid', 'add-id'): pubkeymanager.add_ID,
|
||||||
|
('changeid', 'change-id'): pubkeymanager.change_ID,
|
||||||
|
('resetplugins', 'reset-plugins'): resetplugins.reset
|
||||||
}
|
}
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ Try to provide recommendations for invalid Onionr commands
|
|||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
from difflib import SequenceMatcher
|
from difflib import SequenceMatcher
|
||||||
from logger import log as logging
|
import logger
|
||||||
from . import arguments
|
from . import arguments
|
||||||
"""
|
"""
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
@ -30,9 +30,9 @@ def recommend(print_default: bool = True):
|
|||||||
for key in args.keys():
|
for key in args.keys():
|
||||||
for word in key:
|
for word in key:
|
||||||
if SequenceMatcher(None, tried, word).ratio() >= 0.75:
|
if SequenceMatcher(None, tried, word).ratio() >= 0.75:
|
||||||
logging.warn(f'{print_message} "{tried}", '
|
logger.warn(f'{print_message} "{tried}", '
|
||||||
+ f'did you mean "{word}"?',
|
+ f'did you mean "{word}"?',
|
||||||
)
|
terminal=True)
|
||||||
return
|
return
|
||||||
if print_default:
|
if print_default:
|
||||||
logging.error('%s "%s"' % (print_message, tried))
|
logger.error('%s "%s"' % (print_message, tried), terminal=True)
|
||||||
|
95
src/onionrcommands/pubkeymanager.py
Executable file
95
src/onionrcommands/pubkeymanager.py
Executable file
@ -0,0 +1,95 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
This module defines user ID-related CLI commands
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import getpass
|
||||||
|
|
||||||
|
import unpaddedbase32
|
||||||
|
import niceware
|
||||||
|
|
||||||
|
import logger
|
||||||
|
import onionrexceptions
|
||||||
|
from onionrutils import stringvalidators, bytesconverter
|
||||||
|
import config
|
||||||
|
import keymanager
|
||||||
|
import onionrcrypto
|
||||||
|
import onionrvalues
|
||||||
|
"""
|
||||||
|
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/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
DETERMINISTIC_REQUIREMENT = onionrvalues.PASSWORD_LENGTH
|
||||||
|
|
||||||
|
|
||||||
|
def add_ID():
|
||||||
|
"""Command to create a new user ID key pair."""
|
||||||
|
key_manager = keymanager.KeyManager()
|
||||||
|
pw = ""
|
||||||
|
try:
|
||||||
|
sys.argv[2] # pylint: disable=W0104
|
||||||
|
if not sys.argv[2].lower() == 'true':
|
||||||
|
raise ValueError
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
newID = key_manager.addKey()[0]
|
||||||
|
else:
|
||||||
|
pw = "-".join(niceware.generate_passphrase(32))
|
||||||
|
newID, privKey = onionrcrypto.generate_deterministic(pw)
|
||||||
|
try:
|
||||||
|
key_manager.addKey(pubKey=newID,
|
||||||
|
privKey=privKey)
|
||||||
|
except ValueError:
|
||||||
|
logger.error(
|
||||||
|
'That ID is already available, you can change to it ' +
|
||||||
|
'with the change-id command.', terminal=True)
|
||||||
|
return
|
||||||
|
if pw:
|
||||||
|
print("Phrase to restore ID:", pw)
|
||||||
|
logger.info('Added ID: %s' %
|
||||||
|
(bytesconverter.bytes_to_str(newID.replace('=', '')),), terminal=True)
|
||||||
|
|
||||||
|
|
||||||
|
add_ID.onionr_help = "If the first argument is true, " # type: ignore
|
||||||
|
add_ID.onionr_help += "Onionr will show a deterministic " # type: ignore
|
||||||
|
add_ID.onionr_help += "generation prompt. Otherwise it will " # type: ignore
|
||||||
|
add_ID.onionr_help += "generate & save a new random key pair." # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def change_ID():
|
||||||
|
"""Command to change active ID from argv or stdin."""
|
||||||
|
key_manager = keymanager.KeyManager()
|
||||||
|
try:
|
||||||
|
key = sys.argv[2]
|
||||||
|
key = unpaddedbase32.repad(key.encode()).decode()
|
||||||
|
except IndexError:
|
||||||
|
logger.warn('Specify pubkey to use', terminal=True)
|
||||||
|
else:
|
||||||
|
if stringvalidators.validate_pub_key(key):
|
||||||
|
key_list = key_manager.getPubkeyList()
|
||||||
|
if key in key_list or key.replace('=', '') in key_list:
|
||||||
|
config.set('general.public_key', key)
|
||||||
|
config.save()
|
||||||
|
logger.info('Set active key to: %s' % (key,), terminal=True)
|
||||||
|
logger.info('Restart Onionr if it is running.', terminal=True)
|
||||||
|
else:
|
||||||
|
logger.warn('That key does not exist', terminal=True)
|
||||||
|
else:
|
||||||
|
logger.warn('Invalid key %s' % (key,), terminal=True)
|
||||||
|
|
||||||
|
|
||||||
|
change_ID.onionr_help = "<pubkey>: Switches Onionr to " # type: ignore
|
||||||
|
change_ID.onionr_help += "use a different user ID key. " # type: ignore
|
||||||
|
change_ID.onionr_help += "You should immediately restart " # type: ignore
|
||||||
|
change_ID.onionr_help += "Onionr if it is running." # type: ignore
|
@ -6,7 +6,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from utils import identifyhome
|
from utils import identifyhome
|
||||||
from logger import log as logging
|
import logger
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -32,7 +32,7 @@ def reset():
|
|||||||
if os.path.exists(plugin_dir):
|
if os.path.exists(plugin_dir):
|
||||||
shutil.rmtree(plugin_dir)
|
shutil.rmtree(plugin_dir)
|
||||||
|
|
||||||
logging.info('Default plugins have been reset.')
|
logger.info('Default plugins have been reset.', terminal=True)
|
||||||
|
|
||||||
|
|
||||||
reset.onionr_help = "reinstalls default Onionr plugins" # type: ignore
|
reset.onionr_help = "reinstalls default Onionr plugins" # type: ignore
|
||||||
|
@ -5,7 +5,7 @@ Command to show version info
|
|||||||
import platform
|
import platform
|
||||||
from utils import identifyhome
|
from utils import identifyhome
|
||||||
import onionrvalues
|
import onionrvalues
|
||||||
from logger import log as logging
|
import logger
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -22,21 +22,24 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def version(verbosity=5, function=logging.info):
|
def version(verbosity=5, function=logger.info):
|
||||||
"""Display the Onionr version."""
|
"""Display the Onionr version."""
|
||||||
function('Onionr v%s (%s)' % (onionrvalues.ONIONR_VERSION,
|
function('Onionr v%s (%s) (API v%s)' % (onionrvalues.ONIONR_VERSION,
|
||||||
platform.machine()))
|
platform.machine(),
|
||||||
|
onionrvalues.API_VERSION),
|
||||||
|
terminal=True)
|
||||||
if verbosity >= 1:
|
if verbosity >= 1:
|
||||||
function(onionrvalues.ONIONR_TAGLINE)
|
function(onionrvalues.ONIONR_TAGLINE, terminal=True)
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
pf = platform.platform()
|
pf = platform.platform()
|
||||||
release = platform.release()
|
release = platform.release()
|
||||||
python_imp = platform.python_implementation()
|
python_imp = platform.python_implementation()
|
||||||
python_version = platform.python_version()
|
python_version = platform.python_version()
|
||||||
function(
|
function(
|
||||||
f'{python_imp} {python_version} on {pf} {release}')
|
f'{python_imp} {python_version} on {pf} {release}',
|
||||||
|
terminal=True)
|
||||||
function('Onionr data dir: %s' %
|
function('Onionr data dir: %s' %
|
||||||
identifyhome.identify_home())
|
identifyhome.identify_home(), terminal=True)
|
||||||
|
|
||||||
|
|
||||||
version.onionr_help = 'Shows environment details including ' # type: ignore
|
version.onionr_help = 'Shows environment details including ' # type: ignore
|
||||||
|
27
src/onionrcrypto/__init__.py
Executable file
27
src/onionrcrypto/__init__.py
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
'''
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
This file handles Onionr's cryptography.
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
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 . import generate, hashers, getourkeypair, signing, encryption, cryptoutils
|
||||||
|
generate_deterministic = generate.generate_deterministic
|
||||||
|
generate = generate.generate_pub_key
|
||||||
|
|
||||||
|
keypair = getourkeypair.get_keypair()
|
||||||
|
pub_key = keypair[0]
|
||||||
|
priv_key = keypair[1]
|
6
src/onionrcrypto/cryptoutils/__init__.py
Normal file
6
src/onionrcrypto/cryptoutils/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from . import safecompare, replayvalidation
|
||||||
|
from . import getpubfrompriv
|
||||||
|
|
||||||
|
replay_validator = replayvalidation.replay_timestamp_validation
|
||||||
|
safe_compare = safecompare.safe_compare
|
||||||
|
get_pub_key_from_priv = getpubfrompriv.get_pub_key_from_priv
|
28
src/onionrcrypto/cryptoutils/getpubfrompriv.py
Normal file
28
src/onionrcrypto/cryptoutils/getpubfrompriv.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
generate a public ed25519 key from a private one
|
||||||
|
"""
|
||||||
|
from nacl import signing, encoding
|
||||||
|
|
||||||
|
from onionrtypes import UserID, UserIDSecretKey
|
||||||
|
"""
|
||||||
|
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_pub_key_from_priv(priv_key: UserIDSecretKey,
|
||||||
|
raw_encoding: bool = False) -> UserID:
|
||||||
|
return signing.SigningKey(
|
||||||
|
priv_key, encoder=encoding.Base32Encoder).verify_key.encode(
|
||||||
|
encoding.Base32Encoder)
|
3
src/onionrcrypto/cryptoutils/replayvalidation.py
Normal file
3
src/onionrcrypto/cryptoutils/replayvalidation.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from onionrutils import epoch
|
||||||
|
def replay_timestamp_validation(timestamp):
|
||||||
|
return epoch.get_epoch() - int(timestamp) <= 2419200
|
12
src/onionrcrypto/cryptoutils/safecompare.py
Normal file
12
src/onionrcrypto/cryptoutils/safecompare.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import hmac
|
||||||
|
def safe_compare(one, two):
|
||||||
|
# Do encode here to avoid spawning core
|
||||||
|
try:
|
||||||
|
one = one.encode()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
two = two.encode()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return hmac.compare_digest(one, two)
|
38
src/onionrcrypto/cryptoutils/verifypow.py
Normal file
38
src/onionrcrypto/cryptoutils/verifypow.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
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
|
||||||
|
|
47
src/onionrcrypto/encryption/__init__.py
Normal file
47
src/onionrcrypto/encryption/__init__.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import nacl.encoding, nacl.public, nacl.signing
|
||||||
|
from .. import getourkeypair
|
||||||
|
import unpaddedbase32
|
||||||
|
from onionrutils import bytesconverter, stringvalidators
|
||||||
|
pair = getourkeypair.get_keypair()
|
||||||
|
our_pub_key = unpaddedbase32.repad(pair[0].encode())
|
||||||
|
our_priv_key = unpaddedbase32.repad(pair[1].encode())
|
||||||
|
|
||||||
|
def pub_key_encrypt(data, pubkey, encodedData=False):
|
||||||
|
'''Encrypt to a public key (Curve25519, taken from base32 Ed25519 pubkey)'''
|
||||||
|
pubkey = unpaddedbase32.repad(bytesconverter.str_to_bytes(pubkey))
|
||||||
|
retVal = ''
|
||||||
|
box = None
|
||||||
|
data = bytesconverter.str_to_bytes(data)
|
||||||
|
|
||||||
|
pubkey = nacl.signing.VerifyKey(pubkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_public_key()
|
||||||
|
|
||||||
|
if encodedData:
|
||||||
|
encoding = nacl.encoding.Base64Encoder
|
||||||
|
else:
|
||||||
|
encoding = nacl.encoding.RawEncoder
|
||||||
|
|
||||||
|
box = nacl.public.SealedBox(pubkey)
|
||||||
|
retVal = box.encrypt(data, encoder=encoding)
|
||||||
|
|
||||||
|
return retVal
|
||||||
|
|
||||||
|
def pub_key_decrypt(data, pubkey='', privkey='', encodedData=False):
|
||||||
|
'''pubkey decrypt (Curve25519, taken from Ed25519 pubkey)'''
|
||||||
|
if pubkey != '':
|
||||||
|
pubkey = unpaddedbase32.repad(bytesconverter.str_to_bytes(pubkey))
|
||||||
|
decrypted = False
|
||||||
|
if encodedData:
|
||||||
|
encoding = nacl.encoding.Base64Encoder
|
||||||
|
else:
|
||||||
|
encoding = nacl.encoding.RawEncoder
|
||||||
|
if privkey == '':
|
||||||
|
privkey = our_priv_key
|
||||||
|
ownKey = nacl.signing.SigningKey(seed=privkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()
|
||||||
|
|
||||||
|
if stringvalidators.validate_pub_key(privkey):
|
||||||
|
privkey = nacl.signing.SigningKey(seed=privkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()
|
||||||
|
anonBox = nacl.public.SealedBox(privkey)
|
||||||
|
else:
|
||||||
|
anonBox = nacl.public.SealedBox(ownKey)
|
||||||
|
decrypted = anonBox.decrypt(data, encoder=encoding)
|
||||||
|
return decrypted
|
65
src/onionrcrypto/generate.py
Normal file
65
src/onionrcrypto/generate.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
functions to generate ed25519 key pairs
|
||||||
|
"""
|
||||||
|
import nacl.signing
|
||||||
|
import nacl.encoding
|
||||||
|
import nacl.pwhash
|
||||||
|
|
||||||
|
import onionrexceptions
|
||||||
|
from onionrutils import bytesconverter
|
||||||
|
import onionrvalues
|
||||||
|
"""
|
||||||
|
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 generate_pub_key():
|
||||||
|
"""Generate a Ed25519 public key pair.
|
||||||
|
|
||||||
|
return tuple of base32encoded pubkey, privkey
|
||||||
|
"""
|
||||||
|
private_key = nacl.signing.SigningKey.generate()
|
||||||
|
public_key = private_key.verify_key.encode(
|
||||||
|
encoder=nacl.encoding.Base32Encoder())
|
||||||
|
return (public_key.decode(), private_key.encode(
|
||||||
|
encoder=nacl.encoding.Base32Encoder()).decode())
|
||||||
|
|
||||||
|
|
||||||
|
def generate_deterministic(passphrase, bypassCheck=False):
|
||||||
|
"""Generate a Ed25519 public key pair from a phase.
|
||||||
|
|
||||||
|
not intended for human-generated key
|
||||||
|
"""
|
||||||
|
passStrength = onionrvalues.PASSWORD_LENGTH
|
||||||
|
# Convert to bytes if not already
|
||||||
|
passphrase = bytesconverter.str_to_bytes(passphrase)
|
||||||
|
# Validate passphrase length
|
||||||
|
if not bypassCheck:
|
||||||
|
if len(passphrase) < passStrength:
|
||||||
|
raise onionrexceptions.PasswordStrengthError(
|
||||||
|
"Passphase must be at least %s characters" % (passStrength,))
|
||||||
|
# KDF values
|
||||||
|
kdf = nacl.pwhash.argon2id.kdf
|
||||||
|
# Does not need to be secret, but must be 16 bytes
|
||||||
|
salt = b"U81Q7llrQcdTP0Ux"
|
||||||
|
ops = nacl.pwhash.argon2id.OPSLIMIT_SENSITIVE
|
||||||
|
mem = nacl.pwhash.argon2id.MEMLIMIT_SENSITIVE
|
||||||
|
|
||||||
|
# Generate seed for ed25519 key
|
||||||
|
key = kdf(32, passphrase, salt, opslimit=ops, memlimit=mem)
|
||||||
|
key = nacl.signing.SigningKey(key)
|
||||||
|
return (
|
||||||
|
key.verify_key.encode(nacl.encoding.Base32Encoder).decode(),
|
||||||
|
key.encode(nacl.encoding.Base32Encoder).decode())
|
36
src/onionrcrypto/getourkeypair.py
Normal file
36
src/onionrcrypto/getourkeypair.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
"""
|
||||||
|
Onionr - Private P2P Communication
|
||||||
|
|
||||||
|
returns our current active keypair
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
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 os
|
||||||
|
import keymanager, config, filepaths
|
||||||
|
from . import generate
|
||||||
|
def get_keypair():
|
||||||
|
key_m = keymanager.KeyManager()
|
||||||
|
if os.path.exists(filepaths.keys_file):
|
||||||
|
if len(config.get('general.public_key', '')) > 0:
|
||||||
|
pubKey = config.get('general.public_key')
|
||||||
|
else:
|
||||||
|
pubKey = key_m.getPubkeyList()[0]
|
||||||
|
privKey = key_m.getPrivkey(pubKey)
|
||||||
|
else:
|
||||||
|
keys = generate.generate_pub_key()
|
||||||
|
pubKey = keys[0]
|
||||||
|
privKey = keys[1]
|
||||||
|
key_m.addKey(pubKey, privKey)
|
||||||
|
return (pubKey, privKey)
|
21
src/onionrcrypto/hashers.py
Normal file
21
src/onionrcrypto/hashers.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import hashlib
|
||||||
|
|
||||||
|
import nacl.hash
|
||||||
|
|
||||||
|
|
||||||
|
def sha3_hash(data):
|
||||||
|
try:
|
||||||
|
data = data.encode()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
hasher = hashlib.sha3_256()
|
||||||
|
hasher.update(data)
|
||||||
|
return hasher.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def blake2b_hash(data):
|
||||||
|
try:
|
||||||
|
data = data.encode()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return nacl.hash.blake2b(data)
|
45
src/onionrcrypto/signing/__init__.py
Normal file
45
src/onionrcrypto/signing/__init__.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import base64, binascii
|
||||||
|
|
||||||
|
import unpaddedbase32
|
||||||
|
import nacl.encoding, nacl.signing, nacl.exceptions
|
||||||
|
|
||||||
|
from onionrutils import bytesconverter
|
||||||
|
from onionrutils import mnemonickeys
|
||||||
|
import logger
|
||||||
|
|
||||||
|
def ed_sign(data, key, encodeResult=False):
|
||||||
|
'''Ed25519 sign data'''
|
||||||
|
key = unpaddedbase32.repad(bytesconverter.str_to_bytes(key))
|
||||||
|
try:
|
||||||
|
data = data.encode()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
key = nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
|
||||||
|
retData = ''
|
||||||
|
if encodeResult:
|
||||||
|
retData = key.sign(data, encoder=nacl.encoding.Base64Encoder).signature.decode() # .encode() is not the same as nacl.encoding
|
||||||
|
else:
|
||||||
|
retData = key.sign(data).signature
|
||||||
|
return retData
|
||||||
|
|
||||||
|
def ed_verify(data, key, sig, encodedData=True):
|
||||||
|
'''Verify signed data (combined in nacl) to an ed25519 key'''
|
||||||
|
key = unpaddedbase32.repad(bytesconverter.str_to_bytes(key))
|
||||||
|
try:
|
||||||
|
key = nacl.signing.VerifyKey(key=key, encoder=nacl.encoding.Base32Encoder)
|
||||||
|
except nacl.exceptions.ValueError:
|
||||||
|
return False
|
||||||
|
except binascii.Error:
|
||||||
|
logger.warn('Could not load key for verification, invalid padding')
|
||||||
|
return False
|
||||||
|
retData = False
|
||||||
|
sig = base64.b64decode(sig)
|
||||||
|
try:
|
||||||
|
data = data.encode()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
retData = key.verify(data, sig) # .encode() is not the same as nacl.encoding
|
||||||
|
except nacl.exceptions.BadSignatureError:
|
||||||
|
pass
|
||||||
|
return retData
|
@ -17,13 +17,11 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
import os, re
|
import os, re, importlib
|
||||||
import importlib.util
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from . import onionrevents as events
|
from . import onionrevents as events
|
||||||
from .pluginapis import plugin_apis
|
import config, logger
|
||||||
import config, logging
|
|
||||||
from utils import identifyhome
|
from utils import identifyhome
|
||||||
|
|
||||||
# set data dir
|
# set data dir
|
||||||
@ -44,9 +42,9 @@ def reload(stop_event = True):
|
|||||||
enabled_plugins = get_enabled_plugins()
|
enabled_plugins = get_enabled_plugins()
|
||||||
|
|
||||||
if stop_event is True:
|
if stop_event is True:
|
||||||
logging.debug('Reloading all plugins...')
|
logger.debug('Reloading all plugins...')
|
||||||
else:
|
else:
|
||||||
logging.debug('Loading all plugins...')
|
logger.debug('Loading all plugins...')
|
||||||
|
|
||||||
if stop_event is True:
|
if stop_event is True:
|
||||||
for plugin in enabled_plugins:
|
for plugin in enabled_plugins:
|
||||||
@ -57,7 +55,7 @@ def reload(stop_event = True):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
logging.error('Failed to reload plugins.')
|
logger.error('Failed to reload plugins.')
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -74,8 +72,8 @@ def enable(name, start_event = True):
|
|||||||
except ImportError as e: # Was getting import error on Gitlab CI test "data"
|
except ImportError as e: # Was getting import error on Gitlab CI test "data"
|
||||||
# NOTE: If you are experiencing issues with plugins not being enabled, it might be this resulting from an error in the module
|
# NOTE: If you are experiencing issues with plugins not being enabled, it might be this resulting from an error in the module
|
||||||
# can happen inconsistently (especially between versions)
|
# can happen inconsistently (especially between versions)
|
||||||
logging.error('Failed to enable module:')
|
logger.error('Failed to enable module:', terminal=True)
|
||||||
logging.error(traceback.format_exc())
|
logger.error(traceback.format_exc(), terminal=True)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
enabled_plugins.append(name)
|
enabled_plugins.append(name)
|
||||||
@ -87,8 +85,8 @@ def enable(name, start_event = True):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
logging.error('Failed to enable plugin \"%s\", disabling plugin.' % name)
|
logger.error('Failed to enable plugin \"%s\", disabling plugin.' % name, terminal=True)
|
||||||
logging.debug('Plugins folder not found: %s' % get_plugins_folder(str(name).lower()))
|
logger.debug('Plugins folder not found: %s' % get_plugins_folder(str(name).lower()), terminal=True)
|
||||||
disable(name)
|
disable(name)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -130,9 +128,9 @@ def start(name):
|
|||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
except:
|
except:
|
||||||
logging.error('Failed to start module \"%s\".' % name)
|
logger.error('Failed to start module \"%s\".' % name)
|
||||||
else:
|
else:
|
||||||
logging.error('Failed to start nonexistant module \"%s\".' % name)
|
logger.error('Failed to start nonexistant module \"%s\".' % name)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -154,9 +152,9 @@ def stop(name):
|
|||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
except:
|
except:
|
||||||
logging.error('Failed to stop module \"%s\".' % name)
|
logger.error('Failed to stop module \"%s\".' % name)
|
||||||
else:
|
else:
|
||||||
logging.error('Failed to stop nonexistant module \"%s\".' % name)
|
logger.error('Failed to stop nonexistant module \"%s\".' % name)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -259,11 +257,11 @@ def check():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not config.is_set('plugins'):
|
if not config.is_set('plugins'):
|
||||||
logging.debug('Generating plugin configuration data...')
|
logger.debug('Generating plugin configuration data...')
|
||||||
config.set('plugins', {'enabled': []}, True)
|
config.set('plugins', {'enabled': []}, True)
|
||||||
|
|
||||||
if not os.path.exists(os.path.dirname(get_plugins_folder())):
|
if not os.path.exists(os.path.dirname(get_plugins_folder())):
|
||||||
logging.debug('Generating plugin data folder...')
|
logger.debug('Generating plugin data folder...')
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.dirname(get_plugins_folder()))
|
os.makedirs(os.path.dirname(get_plugins_folder()))
|
||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
|
@ -7,7 +7,7 @@ from threading import Thread
|
|||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import config, logging
|
import config, logger
|
||||||
import onionrplugins as plugins
|
import onionrplugins as plugins
|
||||||
from . import onionrpluginapi as pluginapi
|
from . import onionrpluginapi as pluginapi
|
||||||
"""
|
"""
|
||||||
@ -41,11 +41,11 @@ def __event_caller(event_name, data = {}):
|
|||||||
try:
|
try:
|
||||||
call(plugins.get_plugin(plugin), event_name, data, get_pluginapi(data))
|
call(plugins.get_plugin(plugin), event_name, data, get_pluginapi(data))
|
||||||
except ModuleNotFoundError as _:
|
except ModuleNotFoundError as _:
|
||||||
logging.warn('Disabling nonexistant plugin "%s"...' % plugin)
|
logger.warn('Disabling nonexistant plugin "%s"...' % plugin, terminal=True)
|
||||||
plugins.disable(plugin, stop_event = False)
|
plugins.disable(plugin, stop_event = False)
|
||||||
except Exception as _:
|
except Exception as _:
|
||||||
logging.error('Event "%s" failed for plugin "%s".' % (event_name, plugin))
|
logger.error('Event "%s" failed for plugin "%s".' % (event_name, plugin), terminal=True)
|
||||||
logging.error('\n' + traceback.format_exc())
|
logger.error('\n' + traceback.format_exc(), terminal=True)
|
||||||
|
|
||||||
def event(event_name, data = {}, threaded = True):
|
def event(event_name, data = {}, threaded = True):
|
||||||
"""Call an event on all plugins (if defined)"""
|
"""Call an event on all plugins (if defined)"""
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import onionrplugins, logging
|
import onionrplugins, logger
|
||||||
|
|
||||||
|
|
||||||
class PluginAPI:
|
class PluginAPI:
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
#
|
|
||||||
from typing import Callable, Dict
|
|
||||||
|
|
||||||
# plugin apis are methods intended to be available to the rpc
|
|
||||||
# plugin, this is so plugins can provide apis to other plugins
|
|
||||||
# plugins add their methods during or before afterinit event
|
|
||||||
plugin_apis: Dict[str, Callable] = {}
|
|
120
src/onionrsetup/dbcreator.py
Executable file
120
src/onionrsetup/dbcreator.py
Executable file
@ -0,0 +1,120 @@
|
|||||||
|
"""Onionr - Private P2P Communication.
|
||||||
|
|
||||||
|
DBCreator, creates sqlite3 databases used by 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/>.
|
||||||
|
"""
|
||||||
|
import sqlite3, os
|
||||||
|
from coredb import dbfiles
|
||||||
|
import filepaths
|
||||||
|
|
||||||
|
def createAddressDB():
|
||||||
|
'''
|
||||||
|
Generate the address database
|
||||||
|
|
||||||
|
types:
|
||||||
|
1: I2P b32 address
|
||||||
|
2: Tor v2 (like facebookcorewwwi.onion)
|
||||||
|
3: Tor v3
|
||||||
|
'''
|
||||||
|
if os.path.exists(dbfiles.address_info_db):
|
||||||
|
raise FileExistsError("Address database already exists")
|
||||||
|
conn = sqlite3.connect(dbfiles.address_info_db)
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute('''CREATE TABLE adders(
|
||||||
|
address text,
|
||||||
|
type int,
|
||||||
|
knownPeer text,
|
||||||
|
speed int,
|
||||||
|
success int,
|
||||||
|
powValue text,
|
||||||
|
failure int,
|
||||||
|
lastConnect int,
|
||||||
|
lastConnectAttempt int,
|
||||||
|
trust int,
|
||||||
|
introduced int
|
||||||
|
);
|
||||||
|
''')
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def createPeerDB():
|
||||||
|
'''
|
||||||
|
Generate the peer sqlite3 database and populate it with the peers table.
|
||||||
|
'''
|
||||||
|
if os.path.exists(dbfiles.user_id_info_db):
|
||||||
|
raise FileExistsError("User database already exists")
|
||||||
|
# generate the peer database
|
||||||
|
conn = sqlite3.connect(dbfiles.user_id_info_db)
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute('''CREATE TABLE peers(
|
||||||
|
ID text not null,
|
||||||
|
name text,
|
||||||
|
adders text,
|
||||||
|
dateSeen not null,
|
||||||
|
trust int,
|
||||||
|
hashID text);
|
||||||
|
''')
|
||||||
|
c.execute('''CREATE TABLE forwardKeys(
|
||||||
|
peerKey text not null,
|
||||||
|
forwardKey text not null,
|
||||||
|
date int not null,
|
||||||
|
expire int not null
|
||||||
|
);''')
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def createForwardKeyDB():
|
||||||
|
'''
|
||||||
|
Create the forward secrecy key db (*for *OUR* keys*)
|
||||||
|
'''
|
||||||
|
if os.path.exists(dbfiles.forward_keys_db):
|
||||||
|
raise FileExistsError("Block database already exists")
|
||||||
|
conn = sqlite3.connect(dbfiles.forward_keys_db)
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute('''CREATE TABLE myForwardKeys(
|
||||||
|
peer text not null,
|
||||||
|
publickey text not null,
|
||||||
|
privatekey text not null,
|
||||||
|
date int not null,
|
||||||
|
expire int not null
|
||||||
|
);
|
||||||
|
''')
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def create_blacklist_db():
|
||||||
|
if os.path.exists(dbfiles.blacklist_db):
|
||||||
|
raise FileExistsError("Blacklist db already exists")
|
||||||
|
conn = sqlite3.connect(dbfiles.blacklist_db, timeout=10)
|
||||||
|
c = conn.cursor()
|
||||||
|
# Create table
|
||||||
|
c.execute('''CREATE TABLE blacklist(
|
||||||
|
hash text primary key not null,
|
||||||
|
dataType int,
|
||||||
|
blacklistDate int,
|
||||||
|
expire int
|
||||||
|
);
|
||||||
|
''')
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
create_funcs = [createAddressDB, createPeerDB,
|
||||||
|
createForwardKeyDB, create_blacklist_db]
|
@ -20,16 +20,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import os, shutil
|
import os, shutil
|
||||||
|
|
||||||
import onionrplugins as plugins
|
import onionrplugins as plugins
|
||||||
from logger import log as logging
|
import logger
|
||||||
import filepaths
|
import filepaths
|
||||||
from utils.readstatic import get_static_dir
|
from utils.readstatic import get_static_dir
|
||||||
|
|
||||||
def setup_default_plugins():
|
def setup_default_plugins():
|
||||||
# Copy default plugins into plugins folder
|
# Copy default plugins into plugins folder
|
||||||
if os.path.exists(get_static_dir() + '/official-plugins/'):
|
if os.path.exists(get_static_dir() + '/default-plugins/'):
|
||||||
names = [f for f in os.listdir(get_static_dir() + '/official-plugins/')]
|
names = [f for f in os.listdir(get_static_dir() + '/default-plugins/')]
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
get_static_dir() + '/official-plugins/',
|
get_static_dir() + '/default-plugins/',
|
||||||
plugins.get_plugins_folder(), dirs_exist_ok=True)
|
plugins.get_plugins_folder(), dirs_exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
@ -38,9 +38,9 @@ def setup_default_plugins():
|
|||||||
if not name in plugins.get_enabled_plugins():
|
if not name in plugins.get_enabled_plugins():
|
||||||
plugins.enable(name)
|
plugins.enable(name)
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logger.error(
|
||||||
"Plugin source directory does not exist!" +
|
"Plugin source directory does not exist!" +
|
||||||
"Onionr needs plugins to be useful")
|
"Onionr needs plugins to be useful", terminal=True)
|
||||||
|
|
||||||
|
|
||||||
for name in plugins.get_enabled_plugins():
|
for name in plugins.get_enabled_plugins():
|
||||||
@ -50,5 +50,5 @@ def setup_default_plugins():
|
|||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
#logging.warn('Error enabling plugin: ' + str(e))
|
#logger.warn('Error enabling plugin: ' + str(e), terminal=True)
|
||||||
plugins.disable(name, stop_event = False)
|
plugins.disable(name, stop_event = False)
|
||||||
|
@ -8,8 +8,9 @@ import base64
|
|||||||
import ujson as json
|
import ujson as json
|
||||||
|
|
||||||
import config
|
import config
|
||||||
from logger import log as logging
|
import logger
|
||||||
import onionrvalues
|
import onionrvalues
|
||||||
|
from logger.settings import *
|
||||||
from utils import readstatic
|
from utils import readstatic
|
||||||
"""
|
"""
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
@ -36,3 +37,42 @@ def setup_config():
|
|||||||
config.save()
|
config.save()
|
||||||
|
|
||||||
config.reload()
|
config.reload()
|
||||||
|
|
||||||
|
settings = 0b000
|
||||||
|
if config.get('log.console.color', True):
|
||||||
|
settings = settings | USE_ANSI
|
||||||
|
if config.get('log.console.output', True):
|
||||||
|
settings = settings | OUTPUT_TO_CONSOLE
|
||||||
|
if config.get('log.file.output', True):
|
||||||
|
settings = settings | OUTPUT_TO_FILE
|
||||||
|
set_settings(settings)
|
||||||
|
|
||||||
|
verbosity = str(config.get('log.verbosity', 'default')).lower().strip()
|
||||||
|
if not verbosity in ['default', 'null', 'none', 'nil']:
|
||||||
|
map = {
|
||||||
|
str(LEVEL_DEBUG) : LEVEL_DEBUG,
|
||||||
|
'verbose' : LEVEL_DEBUG,
|
||||||
|
'debug' : LEVEL_DEBUG,
|
||||||
|
str(LEVEL_INFO) : LEVEL_INFO,
|
||||||
|
'info' : LEVEL_INFO,
|
||||||
|
'information' : LEVEL_INFO,
|
||||||
|
str(LEVEL_WARN) : LEVEL_WARN,
|
||||||
|
'warn' : LEVEL_WARN,
|
||||||
|
'warning' : LEVEL_WARN,
|
||||||
|
'warnings' : LEVEL_WARN,
|
||||||
|
str(LEVEL_ERROR) : LEVEL_ERROR,
|
||||||
|
'err' : LEVEL_ERROR,
|
||||||
|
'error' : LEVEL_ERROR,
|
||||||
|
'errors' : LEVEL_ERROR,
|
||||||
|
str(LEVEL_FATAL) : LEVEL_FATAL,
|
||||||
|
'fatal' : LEVEL_FATAL,
|
||||||
|
str(LEVEL_IMPORTANT) : LEVEL_IMPORTANT,
|
||||||
|
'silent' : LEVEL_IMPORTANT,
|
||||||
|
'quiet' : LEVEL_IMPORTANT,
|
||||||
|
'important' : LEVEL_IMPORTANT
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbosity in map:
|
||||||
|
set_level(map[verbosity])
|
||||||
|
else:
|
||||||
|
logger.warn('Verbosity level %s is not valid, using default verbosity.' % verbosity)
|
||||||
|
@ -6,7 +6,7 @@ from threading import Thread
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from logger import log as logging
|
import logger
|
||||||
|
|
||||||
|
|
||||||
def _onionr_thread(func: Callable,
|
def _onionr_thread(func: Callable,
|
||||||
@ -18,10 +18,10 @@ def _onionr_thread(func: Callable,
|
|||||||
try:
|
try:
|
||||||
func(*args, **kwargs)
|
func(*args, **kwargs)
|
||||||
except Exception as _: # noqa
|
except Exception as _: # noqa
|
||||||
logging.warn(
|
logger.warn(
|
||||||
f"Onionr thread exception in {thread_id} \n" +
|
f"Onionr thread exception in {thread_id} \n" +
|
||||||
traceback.format_exc(),
|
traceback.format_exc(),
|
||||||
)
|
terminal=True)
|
||||||
sleep(sleep_secs)
|
sleep(sleep_secs)
|
||||||
|
|
||||||
|
|
||||||
|
22
src/onionrtypes/__init__.py
Normal file
22
src/onionrtypes/__init__.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from typing import NewType
|
||||||
|
|
||||||
|
UserID = NewType('UserID', str)
|
||||||
|
UserIDSecretKey = NewType('UserIDSecretKey', str)
|
||||||
|
|
||||||
|
LoopBackIP = NewType('LoopBackIP', str)
|
||||||
|
|
||||||
|
DeterministicKeyPassphrase = NewType('DeterministicKeyPassphrase', str)
|
||||||
|
|
||||||
|
Ed25519PublicKeyBytes = NewType('Ed25519PublicKeyBytes', bytes)
|
||||||
|
|
||||||
|
BlockHash = NewType('BlockHash', str)
|
||||||
|
|
||||||
|
OnboardingConfig = NewType('OnboardingConfig', str)
|
||||||
|
|
||||||
|
# JSON serializable string. e.g. no raw bytes
|
||||||
|
JSONSerializable = NewType('JSONSerializable', str)
|
||||||
|
|
||||||
|
# Return value of some functions or methods, denoting operation success
|
||||||
|
# Do not use for new code
|
||||||
|
BooleanSuccessState = NewType('BooleanSuccessState', bool)
|
||||||
|
|
9
src/onionrusers/README.md
Executable file
9
src/onionrusers/README.md
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
# onionrusers
|
||||||
|
|
||||||
|
onionrusers is a small collection of classes for interacting with onionr public keys, such as encrypting messages to them with forward secrecy, interacting with their settings, or else.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
onionrusers.py: OnionrUsers class can be used to encrypt/decrypt messages to a particular Onionr user (incl. forward secrecy), view information about them, and get our friend list.
|
||||||
|
|
||||||
|
contactmanager.py: Inheriting from OnionrUsers, ContactManager allows arbitrary information to be associated with an Onionr user.
|
0
static-data/official-plugins/tor/bootstrap.txt → src/onionrusers/__init__.py
Normal file → Executable file
0
static-data/official-plugins/tor/bootstrap.txt → src/onionrusers/__init__.py
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user