Documentation

This commit is contained in:
Davte 2020-04-13 20:57:39 +02:00
parent 3f5384f9e9
commit d703054c58
4 changed files with 118 additions and 24 deletions

View File

@ -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 = '.'
```

View File

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

View File

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

View File

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