import json import pathlib import hashlib import requests import base64 import sys from os import geteuid import subprocess def compute_file_hash(filepath): with open(filepath, 'rb') as file_for_hash: data = file_for_hash.read() filesize = len(data) # Github sha value is computed as such: return hashlib.sha1(b"blob " + bytes(str(filesize), 'utf-8') + b"\0" + data).hexdigest() def get_master_info(repo, filepath, credz): url = f"https://api.github.com/repos/{repo}/contents/{filepath}" r = requests.get(url, auth=credz) sha = r.json()['sha'] content = r.json()['content'] return sha, content def get_last_release_info(repo, credz): url = f"https://api.github.com/repos/{repo}/releases" r = requests.get(url, auth=credz) for release in r.json(): if not release['draft'] and not release['prerelease']: return release['tag_name'] def githubmastersync(reponame, filepaths, credz): for filepath in filepaths: localfile = pathlib.Path('files').joinpath(pathlib.Path(filepath).name) print(f" * {localfile} ", end='') lastsha, content = get_master_info(reponame, filepath, credz) if not localfile.exists(): content = base64.b64decode(content) with open(localfile, 'wb') as f: f.write(content) print('-> Installed! ;)') else: sha = compute_file_hash(localfile) if sha == lastsha: print('-> Up-to-date.') else: content = base64.b64decode(content) with open(localfile, 'wb') as f: f.write(content) print('-> Updated!') def githubreleasesync(reponame, repoinfo, credz): local_version = repoinfo['local_version'] last_version = get_last_release_info(reponame, credz) filenames = repoinfo['files'] for filename in filenames: localfile = pathlib.Path('files').joinpath(pathlib.Path(filename).name) urldl = f'https://github.com/{reponame}/releases/download/{last_version}/{filename}' print(f" * {localfile} ", end='') if not localfile.exists(): content = requests.get(urldl, auth=credz).content with open(localfile, 'wb') as f: f.write(content) print('-> Installed! ;)') else: if local_version == last_version: print('-> Up-to-date.') else: content = requests.get(urldl, auth=credz).content with open(localfile, 'wb') as f: f.write(content) with open("config.json", "r") as jsonfile: data = json.load(jsonfile) data['githubreleasesync'][reponame]['local_version'] = last_version with open("config.json", "w") as jsonfile: json.dump(data, jsonfile, indent=4) print('-> Updated!') def update(config): print("Updating...") with open("credz.json", "r") as jsonfile: credz = json.load(jsonfile) credz = (credz['username'], credz['token']) for reponame, filepaths in config['githubmastersync'].items(): githubmastersync(reponame, filepaths, credz) for reponame, repoinfo in config['githubreleasesync'].items(): githubreleasesync(reponame, repoinfo, credz) def print_menu(menu_options): for key in menu_options.keys(): print(key, '->', menu_options[key]) def is_sudo(): return True if geteuid() == 0 else False def ask_port(default_port): while True: try: choice = input(f'What port would you like to listen on ? [{default_port}]: ') if choice == '': port = default_port else: port = int(choice) break except ValueError: print('Wrong input. Please enter a number ...') return port def listen_http(files_dir): port = ask_port(80) ips = get_ips() print('The HTTP server is about to start.\nAvailable endpoints:') for iname in ips: if port == 80: print(f' -> {iname}: http://{ips[iname]}/') else: print(f' -> {iname}: http://{ips[iname]}:{port}/') if port < 1024 and not is_sudo(): print('Listening on any port under 1024 requires root permissions.') cmd = ['sudo', 'python', '-m', 'http.server', '-d', files_dir, str(port)] else: cmd = ['python', '-m', 'http.server', '-d', files_dir, str(port)] subprocess.call(cmd) def listen_smb(files_dir): port = ask_port(445) ips = get_ips() print('The HTTP server is about to start.\nAvailable endpoints:') for iname in ips: if port == 445: print(f' -> {iname}: \\\\{ips[iname]}\\share\\') else: print(f' -> {iname}: \\\\{ips[iname]}:{port}\\share\\ # This syntax (:port) is not supported on Windows ?') if port < 1024 and not is_sudo(): print('Listening on any port under 1024 requires root permissions.') cmd = ['sudo', 'smbserver.py', '-port', str(port), 'share', files_dir] else: cmd = ['smbserver.py', '-port', str(port), 'share', files_dir] subprocess.call(cmd) def get_public_ip(): return requests.get('https://api.ipify.org').text def get_ips(): ips = dict() try: ips['public'] = get_public_ip() except requests.exceptions.ConnectionError: pass cmd = ['ip', 'a'] out = subprocess.run(cmd, capture_output=True).stdout.decode('utf-8') lines = out.split('\n') while("" in lines): lines.remove("") for line in lines: if line[0].isdigit(): iname = line.split(' ')[1].split(':')[0] if 'inet ' in line: ip = line.split(' ')[5].split('/')[0] if ip != '127.0.0.1': ips[iname] = ip return ips def menu_choice(menu_options): while(True): print_menu(menu_options) option = '' try: option = int(input('Enter your choice: ')) except ValueError: print('Wrong input. Please enter a number ...') files_dir = pathlib.Path.cwd().joinpath('files') if option == 1: listen_http(files_dir) elif option == 2: listen_smb(files_dir) elif option == 0: sys.exit('Quitting') else: print('Invalid option. Please enter a valid number.') if __name__ == '__main__': with open("config.json", "r") as jsonfile: config = json.load(jsonfile) update(config) menu_options = { 1: 'HTTP', 2: 'SMB', 0: 'Exit', } menu_choice(menu_options)