Documentation
This commit is contained in:
parent
3f5384f9e9
commit
d703054c58
40
README.md
40
README.md
@ -1,6 +1,6 @@
|
||||
# filebridging
|
||||
|
||||
Share files via a bridge server using TCP over SSL and aes-256-cbc encryption.
|
||||
Share files via a bridge server using TCP over SSL and end-to-end encryption.
|
||||
|
||||
## Requirements
|
||||
Python3.8+ is needed for this package.
|
||||
@ -21,15 +21,33 @@ python -m filebridging.client --help
|
||||
```
|
||||
|
||||
## Examples
|
||||
Client-server example
|
||||
```bash
|
||||
# 3 distinct tabs
|
||||
python -m filebridging.server --host localhost --port 5000 --certificate ~/.ssh/server.crt --key ~/.ssh/server.key
|
||||
python -m filebridging.client s --host localhost --port 5000 --certificate ~/.ssh/server.crt --token 12345678 --password supersecretpasswordhere --path ~/file_to_send
|
||||
python -m filebridging.client r --host localhost --port 5000 --certificate ~/.ssh/server.crt --token 12345678 --password supersecretpasswordhere --path ~/Downloads
|
||||
```
|
||||
* Client-server example
|
||||
```bash
|
||||
# 3 distinct tabs
|
||||
python -m filebridging.server --host localhost --port 5000 --certificate ~/.ssh/server.crt --key ~/.ssh/server.key
|
||||
python -m filebridging.client s --host localhost --port 5000 --certificate ~/.ssh/server.crt --token 12345678 --password supersecretpasswordhere --path ~/file_to_send
|
||||
python -m filebridging.client r --host localhost --port 5000 --certificate ~/.ssh/server.crt --token 12345678 --password supersecretpasswordhere --path ~/Downloads
|
||||
```
|
||||
|
||||
Client-client example
|
||||
```bash
|
||||
* Client-client example
|
||||
```bash
|
||||
# 2 distinct tabs
|
||||
python -m filebridging.client s --host localhost --port 5000 --certificate ~/.ssh/server.crt --key ~/.ssh/private.key --token 12345678 --password supersecretpasswordhere --path ~/file_to_send --standalone
|
||||
python -m filebridging.client r --host localhost --port 5000 --certificate ~/.ssh/server.crt --token 12345678 --password supersecretpasswordhere --path ~/Downloads
|
||||
```
|
||||
The receiver client may be standalone as well: just add the `--key` parameter (for SSL-secured sessions) and the `--standalone` flag.
|
||||
|
||||
```
|
||||
* Configuration file example
|
||||
```python
|
||||
#!/bin/python
|
||||
|
||||
host = "www.example.com"
|
||||
port = 5000
|
||||
certificate = "/path/to/public.crt"
|
||||
key = "/path/to/private.key"
|
||||
|
||||
action = 'r'
|
||||
password = 'verysecretpassword'
|
||||
token = 'sessiontok'
|
||||
file_path = '.'
|
||||
```
|
||||
|
@ -1,4 +1,17 @@
|
||||
"""Receiver and sender client class."""
|
||||
"""Receiver and sender client class.
|
||||
|
||||
Arguments
|
||||
- host: localhost, IPv4 address or domain (e.g. www.example.com)
|
||||
- port: port to reach (must be enabled)
|
||||
- action: either [S]end or [R]eceive
|
||||
- file_path: file to send / destination folder
|
||||
- token: session token (6-10 alphanumerical characters)
|
||||
- certificate [optional]: server certificate for SSL
|
||||
- key [optional]: needed only for standalone clients
|
||||
- password [optional]: necessary to end-to-end encryption
|
||||
- standalone [optional]: allow client-to-client communication (the host
|
||||
must be reachable by both clients)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
@ -15,6 +28,11 @@ from . import utilities
|
||||
|
||||
|
||||
class Client:
|
||||
"""Sender or receiver client.
|
||||
|
||||
Create a Client object providing host, port and other optional parameters.
|
||||
Then, run it with `Client().run()` method
|
||||
"""
|
||||
def __init__(self, host='localhost', port=5000, ssl_context=None,
|
||||
action=None,
|
||||
standalone=False,
|
||||
@ -49,10 +67,15 @@ class Client:
|
||||
|
||||
@property
|
||||
def host(self) -> str:
|
||||
"""Host to reach.
|
||||
|
||||
For standalone clients, you must be able to listen this host.
|
||||
"""
|
||||
return self._host
|
||||
|
||||
@property
|
||||
def port(self) -> int:
|
||||
"""Port number."""
|
||||
return self._port
|
||||
|
||||
@property
|
||||
@ -84,14 +107,25 @@ class Client:
|
||||
|
||||
@property
|
||||
def buffer_length_limit(self) -> int:
|
||||
"""Max number of buffer chunks in memory.
|
||||
|
||||
You may want to reduce this limit to allocate less memory, or increase
|
||||
it to boost performance.
|
||||
"""
|
||||
return self._buffer_length_limit
|
||||
|
||||
@property
|
||||
def buffer_chunk_size(self) -> int:
|
||||
"""Length (bytes) of buffer chunks in memory.
|
||||
|
||||
You may want to reduce this limit to allocate less memory, or increase
|
||||
it to boost performance.
|
||||
"""
|
||||
return self._buffer_chunk_size
|
||||
|
||||
@property
|
||||
def file_path(self) -> str:
|
||||
"""Path of file to send or destination folder."""
|
||||
return self._file_path
|
||||
|
||||
@property
|
||||
@ -107,6 +141,11 @@ class Client:
|
||||
|
||||
@property
|
||||
def token(self):
|
||||
"""Session token.
|
||||
|
||||
6-10 alphanumerical characters to provide to server to link sender and
|
||||
receiver.
|
||||
"""
|
||||
return self._token
|
||||
|
||||
@property
|
||||
@ -128,6 +167,7 @@ class Client:
|
||||
|
||||
@property
|
||||
def file_size_string(self):
|
||||
"""Formatted file size (e.g. 64.22 MB)."""
|
||||
return self._file_size_string
|
||||
|
||||
async def run_client(self) -> None:
|
||||
@ -168,6 +208,13 @@ class Client:
|
||||
|
||||
async def _connect(self, reader: asyncio.StreamReader,
|
||||
writer: asyncio.StreamWriter):
|
||||
"""Wrap connect method to catch exceptions.
|
||||
|
||||
This is required since callbacks are never awaited and potential
|
||||
exception would be logged at loop.close().
|
||||
Only standalone clients need this wrapper, regular clients might use
|
||||
connect method directly.
|
||||
"""
|
||||
try:
|
||||
return await self.connect(reader, writer)
|
||||
except KeyboardInterrupt:
|
||||
@ -178,6 +225,12 @@ class Client:
|
||||
async def connect(self,
|
||||
reader: asyncio.StreamReader,
|
||||
writer: asyncio.StreamWriter):
|
||||
"""Communicate with the server or the other client.
|
||||
|
||||
Send information about the client (connection token, role, file name
|
||||
and size), get information from the server (file name and size), wait
|
||||
for start signal and then send or receive the file.
|
||||
"""
|
||||
self._reader = reader
|
||||
self._writer = writer
|
||||
|
||||
@ -250,6 +303,10 @@ class Client:
|
||||
await self.receive(reader=self.reader)
|
||||
|
||||
async def encrypt_file(self, input_file, output_file):
|
||||
"""Use openssl to encrypt the input_file.
|
||||
|
||||
The encrypted file will overwrite `output_file` if it exists.
|
||||
"""
|
||||
self._encryption_complete = False
|
||||
logging.info("Encrypting file...")
|
||||
stdout, stderr = ''.encode(), ''.encode()
|
||||
@ -273,6 +330,11 @@ class Client:
|
||||
self._encryption_complete = True
|
||||
|
||||
async def send(self, writer: asyncio.StreamWriter):
|
||||
"""Encrypt and send the file.
|
||||
|
||||
Caution: if no password is provided, the file will be sent as clear
|
||||
text.
|
||||
"""
|
||||
self._working = True
|
||||
file_path = self.file_path
|
||||
if self.password:
|
||||
@ -327,6 +389,10 @@ class Client:
|
||||
return
|
||||
|
||||
async def receive(self, reader: asyncio.StreamReader):
|
||||
"""Download the file and decrypt it.
|
||||
|
||||
If no password is provided, the file cannot be decrypted.
|
||||
"""
|
||||
self._working = True
|
||||
file_path = os.path.join(
|
||||
os.path.abspath(
|
||||
@ -355,6 +421,11 @@ class Client:
|
||||
break
|
||||
file_to_receive.write(input_data)
|
||||
print() # New line after sys.stdout.write
|
||||
if bytes_received < self.file_size:
|
||||
logging.warning("Transmission terminated too soon!")
|
||||
if self.password:
|
||||
logging.error("Partial files can not be decrypted!")
|
||||
return
|
||||
logging.info("File received.")
|
||||
if self.password:
|
||||
logging.info("Decrypting file...")
|
||||
@ -688,17 +759,16 @@ def main():
|
||||
else:
|
||||
logging.info("Proceeding without storing values...")
|
||||
ssl_context = None
|
||||
if certificate is not None:
|
||||
if key is None: # Server-dependent client
|
||||
ssl_context = ssl.create_default_context(
|
||||
purpose=ssl.Purpose.SERVER_AUTH
|
||||
)
|
||||
ssl_context.load_verify_locations(certificate)
|
||||
else: # Standalone client
|
||||
if certificate and key and standalone: # Standalone client
|
||||
ssl_context = ssl.create_default_context(
|
||||
purpose=ssl.Purpose.CLIENT_AUTH
|
||||
)
|
||||
ssl_context.load_cert_chain(certificate, key)
|
||||
elif certificate: # Server-dependent client
|
||||
ssl_context = ssl.create_default_context(
|
||||
purpose=ssl.Purpose.SERVER_AUTH
|
||||
)
|
||||
ssl_context.load_verify_locations(certificate)
|
||||
else:
|
||||
logging.warning(
|
||||
"Please consider using SSL. To do so, add in `config.py` or "
|
||||
|
@ -1,6 +1,12 @@
|
||||
"""Server class.
|
||||
|
||||
May be a local server or a publicly reachable server.
|
||||
|
||||
Arguments
|
||||
- host: localhost, IPv4 address or domain (e.g. www.example.com)
|
||||
- port: port to reach (must be enabled)
|
||||
- certificate [optional]: server certificate for SSL
|
||||
- key [optional]: needed only for standalone clients
|
||||
"""
|
||||
|
||||
import argparse
|
||||
|
2
setup.py
2
setup.py
@ -45,7 +45,7 @@ setuptools.setup(
|
||||
author=find_information("author", "filebridging", "__init__.py"),
|
||||
author_email=find_information("email", "filebridging", "__init__.py"),
|
||||
description=(
|
||||
"Share files via a bridge server using TCP over SSL and aes-256-cbc "
|
||||
"Share files via a bridge server using TCP over SSL and end-to-end "
|
||||
"encryption."
|
||||
),
|
||||
license=find_information("license", "filebridging", "__init__.py"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user