From 304c4211ad846451cf782841ad9308c018d961a6 Mon Sep 17 00:00:00 2001 From: Milan Stute Date: Tue, 17 Dec 2019 13:31:27 +0100 Subject: [PATCH] Clean up code --- opendrop/__init__.py | 10 +++++----- opendrop/cli.py | 22 +++++++++++----------- opendrop/client.py | 42 ++++++++++++++++++++++++++++-------------- opendrop/config.py | 44 ++++++++++++++++++++++++++++---------------- opendrop/server.py | 35 ++++++++++++++++------------------- opendrop/util.py | 32 +++++++++++++++----------------- setup.py | 7 ++----- 7 files changed, 105 insertions(+), 87 deletions(-) diff --git a/opendrop/__init__.py b/opendrop/__init__.py index f09a78a..0a88685 100644 --- a/opendrop/__init__.py +++ b/opendrop/__init__.py @@ -18,15 +18,15 @@ along with this program. If not, see . """ import logging -import platform import os +import platform __version__ = '0.10.3' if platform.system() == 'Darwin': - dyld_path = os.environ.get('DYLD_LIBRARY_PATH', '') # save old path - openssl_path = '/usr/local/opt/openssl/lib' - archive_path = '/usr/local/opt/libarchive/lib' - os.environ['DYLD_LIBRARY_PATH'] = '{}:{}:{}'.format(dyld_path, openssl_path, archive_path) + dyld_path = os.environ.get('DYLD_LIBRARY_PATH', '') # save old path + openssl_path = '/usr/local/opt/openssl/lib' + archive_path = '/usr/local/opt/libarchive/lib' + os.environ['DYLD_LIBRARY_PATH'] = '{}:{}:{}'.format(dyld_path, openssl_path, archive_path) logger = logging.getLogger(__name__) diff --git a/opendrop/cli.py b/opendrop/cli.py index 9746f54..5e8714f 100644 --- a/opendrop/cli.py +++ b/opendrop/cli.py @@ -17,15 +17,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import time - -import ipaddress -import logging import argparse -import sys import json +import logging import os +import sys import threading +import time from .client import AirDropBrowser, AirDropClient from .config import AirDropConfig, AirDropReceiverFlags @@ -39,7 +37,6 @@ def main(): class AirDropCli: - def __init__(self, args): parser = argparse.ArgumentParser() parser.add_argument('action', choices=['receive', 'find', 'send']) @@ -60,9 +57,12 @@ class AirDropCli: # TODO put emails and phone in canonical form (lower case, no '+' sign, etc.) - self.config = AirDropConfig(email=args.email, phone=args.phone, - computer_name=args.name, computer_model=args.model, - debug=args.debug, interface=args.interface) + self.config = AirDropConfig(email=args.email, + phone=args.phone, + computer_name=args.name, + computer_model=args.model, + debug=args.debug, + interface=args.interface) self.server = None self.client = None self.browser = None @@ -106,7 +106,7 @@ class AirDropCli: json.dump(self.discover, f) def _found_receiver(self, info): - thread = threading.Thread(target=self._send_discover, args=(info,)) + thread = threading.Thread(target=self._send_discover, args=(info, )) thread.start() def _send_discover(self, info): @@ -193,7 +193,7 @@ class AirDropCli: except IndexError: pass # (2) try 'id' - if len(self.receiver) is 12: + if len(self.receiver) == 12: for info in infos: if info['id'] == self.receiver: return info diff --git a/opendrop/client.py b/opendrop/client.py index 7bd02cd..3258144 100644 --- a/opendrop/client.py +++ b/opendrop/client.py @@ -17,16 +17,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import fleep -from http.client import HTTPSConnection +import io import ipaddress import logging import os -import io -import libarchive import platform import plistlib import socket +from http.client import HTTPSConnection + +import fleep +import libarchive from .util import AirDropUtil, AbsArchiveWrite from .zeroconf import ServiceBrowser, Zeroconf, IPVersion @@ -35,11 +36,10 @@ logger = logging.getLogger(__name__) class AirDropBrowser: - def __init__(self, config): self.ip_addr = AirDropUtil.get_ip_for_interface(config.interface, ipv6=True) if self.ip_addr is None: - if config.interface is 'awdl0': + if config.interface == 'awdl0': raise RuntimeError('Interface {} does not have an IPv6 address. ' 'Make sure that `owl` is running.'.format(config.interface)) else: @@ -80,7 +80,6 @@ class AirDropBrowser: class AirDropClient: - def __init__(self, config, receiver): self.config = config self.receiver_host = receiver[0] @@ -98,7 +97,8 @@ class AirDropClient: _headers[key] = val if self.http_conn is None: # Use single connection - self.http_conn = HTTPSConnectionAWDL(self.receiver_host, self.receiver_port, + self.http_conn = HTTPSConnectionAWDL(self.receiver_host, + self.receiver_port, interface_name=self.config.interface, context=self.config.get_ssl_context()) self.http_conn.request('POST', url, body=body, headers=_headers) @@ -161,6 +161,7 @@ class AirDropClient: 'ConvertMediaFormats': 0 } yield file_entry + ask_body['Files'] = [e for e in file_entries(file_path)] ask_body['Items'] = [] @@ -195,7 +196,7 @@ class AirDropClient: def _get_headers(self): """ - Get the headers for requests sent + Get the headers for requests sent """ headers = { 'Content-Type': 'application/octet-stream', @@ -212,9 +213,17 @@ class HTTPSConnectionAWDL(HTTPSConnection): """ This class allows to bind the HTTPConnection to a specific network interface """ - - def __init__(self, host, port=None, key_file=None, cert_file=None, timeout=None, source_address=None, - *, context=None, check_hostname=None, interface_name=None): + def __init__(self, + host, + port=None, + key_file=None, + cert_file=None, + timeout=None, + source_address=None, + *, + context=None, + check_hostname=None, + interface_name=None): if interface_name is not None: if '%' not in host: @@ -224,8 +233,13 @@ class HTTPSConnectionAWDL(HTTPSConnection): if timeout is None: timeout = socket.getdefaulttimeout() - super(HTTPSConnectionAWDL, self).__init__(host=host, port=port, key_file=key_file, cert_file=cert_file, - timeout=timeout, source_address=source_address, context=context, + super(HTTPSConnectionAWDL, self).__init__(host=host, + port=port, + key_file=key_file, + cert_file=cert_file, + timeout=timeout, + source_address=source_address, + context=context, check_hostname=check_hostname) self.interface_name = interface_name diff --git a/opendrop/config.py b/opendrop/config.py index ef73fef..544a249 100644 --- a/opendrop/config.py +++ b/opendrop/config.py @@ -17,14 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import os import logging -from pkg_resources import resource_filename +import os +import random import socket import ssl -import random import subprocess +from pkg_resources import resource_filename + logger = logging.getLogger(__name__) @@ -45,10 +46,17 @@ class AirDropReceiverFlags: class AirDropConfig: - - def __init__(self, host_name=None, computer_name=None, computer_model=None, server_port=8771, - airdrop_dir='~/.opendrop', service_id=None, - email=None, phone=None, debug=False, interface=None): + def __init__(self, + host_name=None, + computer_name=None, + computer_model=None, + server_port=8771, + airdrop_dir='~/.opendrop', + service_id=None, + email=None, + phone=None, + debug=False, + interface=None): self.airdrop_dir = os.path.expanduser(airdrop_dir) self.discovery_report = os.path.join(self.airdrop_dir, 'discover.last.json') @@ -104,14 +112,18 @@ class AirDropConfig: logger.info('Create new self-signed certificate in {}'.format(self.key_dir)) if not os.path.exists(self.key_dir): os.makedirs(self.key_dir) - subprocess.run(['openssl', 'req', '-newkey', 'rsa:2048', '-nodes', '-keyout', 'key.pem', - '-x509', '-days', '365', '-out', 'certificate.pem', - '-subj', '/CN={}'.format(self.computer_name)], cwd=self.key_dir, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + subprocess.run([ + 'openssl', 'req', '-newkey', 'rsa:2048', '-nodes', '-keyout', 'key.pem', '-x509', '-days', '365', '-out', + 'certificate.pem', '-subj', '/CN={}'.format(self.computer_name) + ], + cwd=self.key_dir, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) def get_ssl_context(self): - sslctxt = ssl.SSLContext() - sslctxt.load_cert_chain(self.cert_file, keyfile=self.key_file) - sslctxt.load_verify_locations(cafile=self.root_ca_file) - sslctxt.verify_mode = ssl.CERT_NONE # we accept self-signed certificates as does Apple - return sslctxt + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS) + ctx.options |= ssl.OP_NO_TLSv1 # TLSv1.0 is insecure + ctx.load_cert_chain(self.cert_file, keyfile=self.key_file) + ctx.load_verify_locations(cafile=self.root_ca_file) + ctx.verify_mode = ssl.CERT_NONE # we accept self-signed certificates as does Apple + return ctx diff --git a/opendrop/server.py b/opendrop/server.py index d747ab5..d55f02b 100644 --- a/opendrop/server.py +++ b/opendrop/server.py @@ -17,20 +17,20 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ import io - +import json import logging import platform import plistlib import socket +import time from http.server import HTTPServer, BaseHTTPRequestHandler -import json + import libarchive import libarchive.extract import libarchive.read -from .zeroconf import Zeroconf, ServiceInfo, IPVersion -import time from .util import AirDropUtil +from .zeroconf import Zeroconf, ServiceInfo, IPVersion logger = logging.getLogger(__name__) @@ -39,7 +39,6 @@ class AirDropServer: """ Announces an HTTPS AirDrop server in the local network via mDNS. """ - def __init__(self, config): self.config = config @@ -50,7 +49,7 @@ class AirDropServer: self.ip_addr = AirDropUtil.get_ip_for_interface(self.config.interface, ipv6=True) if self.ip_addr is None: - if self.config.interface is 'awdl0': + if self.config.interface == 'awdl0': raise RuntimeError('Interface {} does not have an IPv6 address. ' 'Make sure that `owl` is running.'.format(self.config.interface)) else: @@ -68,14 +67,13 @@ class AirDropServer: properties = self.get_properties() server = self.config.host_name + '.local.' service_name = self.config.service_id + '._airdrop._tcp.local.' - info = ServiceInfo( - '_airdrop._tcp.local.', service_name, - self.ip_addr.packed, self.config.port, 0, 0, properties, server) + info = ServiceInfo('_airdrop._tcp.local.', service_name, self.ip_addr.packed, self.config.port, 0, 0, properties, + server) return info def start_service(self): - logger.info('Announcing service: host {}, address {}, port {}'.format(self.config.host_name, - self.ip_addr, self.config.port)) + logger.info('Announcing service: host {}, address {}, port {}'.format(self.config.host_name, self.ip_addr, + self.config.port)) self.zeroconf.register_service(self.service_info) def _init_server(self): @@ -203,8 +201,7 @@ class AirDropServerHandler(BaseHTTPRequestHandler): AirDropUtil.write_debug(self.config, post_data, 'receive_ask_request.plist') - ask_response = {'ReceiverModelName': self.config.computer_model, - 'ReceiverComputerName': self.config.computer_name} + ask_response = {'ReceiverModelName': self.config.computer_model, 'ReceiverComputerName': self.config.computer_name} ask_resp_binary = plistlib.dumps(ask_response, fmt=plistlib.FMT_BINARY) AirDropUtil.write_debug(self.config, ask_resp_binary, 'receive_ask_response.plist') @@ -245,18 +242,18 @@ class AirDropServerHandler(BaseHTTPRequestHandler): self.total = 0 def _next_chunk(self): - if self.chunk is None or len(self.chunk) is 0: + if self.chunk is None or len(self.chunk) == 0: length = int(self.rfile.readline().rstrip(), 16) self.chunk = self.rfile.read(length) self.rfile.readline() # strip trailing \n\r def readinto(self, buf): self._next_chunk() - l = min(len(self.chunk), len(buf)) - buf[:l] = self.chunk[:l] - self.chunk = self.chunk[l:] - self.total += l - return l + length = min(len(self.chunk), len(buf)) + buf[:length] = self.chunk[:length] + self.chunk = self.chunk[length:] + self.total += length + return length def extract_stream(stream, flags=0): """ diff --git a/opendrop/util.py b/opendrop/util.py index dd74a7e..27a8dd6 100644 --- a/opendrop/util.py +++ b/opendrop/util.py @@ -19,13 +19,15 @@ along with this program. If not, see . import base64 import datetime +import hashlib import io import ipaddress import os import plistlib -import hashlib + import ifaddr from PIL import Image, ExifTags +from ctypescrypto import cms, x509, pkey, oid from libarchive import ffi from libarchive.entry import new_archive_entry, ArchiveEntry from libarchive.ffi import ( @@ -39,7 +41,6 @@ from libarchive.ffi import ( write_finish_entry, ) from libarchive.write import ArchiveWrite, new_archive_read_disk -from ctypescrypto import cms, x509, pkey, oid class AirDropUtil: @@ -47,13 +48,12 @@ class AirDropUtil: This class contains a set of utility functions that support the opendrop implementation They have been moved, because the opendrop files tend to get too long """ - @staticmethod def get_uti_type(flp) -> str: """ - Get the Apple conform UTI Type from a flp instance which has been used on the data which should be sent + Get the Apple conform UTI Type from a flp instance which has been used on the data which should be sent - :param flp: fleep object + :param flp: fleep object """ # Default UTI Type @@ -132,8 +132,7 @@ class AirDropUtil: cert = x509.X509(sign_cert_file.read()) key = pkey.PKey(privkey=key_file.read()) # possibly need to add intermediate certs - cms_signed = cms.SignedData.create(record_data_plist, cert=cert, pkey=key, certs=None, - flags=cms.Flags.PARTIAL) + cms_signed = cms.SignedData.create(record_data_plist, cert=cert, pkey=key, certs=None, flags=cms.Flags.PARTIAL) signed_data = AirDropUtil.pem2der(cms_signed.pem()) return signed_data @@ -141,7 +140,7 @@ class AirDropUtil: @staticmethod def pem2der(s): """ - Create DER Formatted bytes from a PEM Base64 String + Create DER Formatted bytes from a PEM Base64 String :param s: PEM formatted string """ @@ -153,10 +152,10 @@ class AirDropUtil: @staticmethod def generate_file_icon(file_path): """ - Generates a small and a big thumbnail of an image + Generates a small and a big thumbnail of an image This will make it possible to preview the sent file - :param file_path: The path to the image + :param file_path: The path to the image """ im = Image.open(file_path) @@ -177,10 +176,10 @@ class AirDropUtil: file_icon = imgByteArr.getvalue() # Small image - #im.thumbnail((64, 64), Image.ANTIALIAS) - #imgByteArr = io.BytesIO() - #im.save(imgByteArr, format='JPEG2000') - #small_file_icon = imgByteArr.getvalue() + # im.thumbnail((64, 64), Image.ANTIALIAS) + # imgByteArr = io.BytesIO() + # im.save(imgByteArr, format='JPEG2000') + # small_file_icon = imgByteArr.getvalue() return file_icon @@ -193,7 +192,6 @@ class AirDropUtil: :param bool ipv6: Boolean indicating if the ipv6 address should be retrieved :return: IPv4Address or IPv6Address object or None """ - def get_interface_by_name(name): for interface in ifaddr.get_adapters(): if interface.name == name: @@ -241,7 +239,7 @@ class AbsArchiveWrite(ArchiveWrite): with new_archive_entry() as entry_p: entry = ArchiveEntry(None, entry_p) with new_archive_read_disk(path) as read_p: - while 1: + while True: r = read_next_header2(read_p, entry_p) if r == ARCHIVE_EOF: break @@ -250,7 +248,7 @@ class AbsArchiveWrite(ArchiveWrite): write_header(write_p, entry_p) try: with open(entry_sourcepath(entry_p), 'rb') as f: - while 1: + while True: data = f.read(block_size) if not data: break diff --git a/setup.py b/setup.py index 926f55a..55cf7d8 100644 --- a/setup.py +++ b/setup.py @@ -32,11 +32,8 @@ setup( ], keywords='cli', packages=find_packages(exclude=['docs']), - package_data={ - 'opendrop': ['certs/*.pem'] - }, - install_requires=['requests', 'fleep', 'ifaddr', 'Pillow', - 'requests_toolbelt', 'ctypescrypto', 'libarchive-c'], + package_data={'opendrop': ['certs/*.pem']}, + install_requires=['requests', 'fleep', 'ifaddr', 'Pillow', 'requests_toolbelt', 'ctypescrypto', 'libarchive-c'], entry_points={ 'console_scripts': [ 'opendrop=opendrop.cli:main',