def list_plugins(args, config, pattern='*'): """ Lists the available and installed plugins """ found = False line = "|{name:^{width}}|{version:^9}|{enabled:^10}|{status:^15}|" available = _get_available() installed = _get_installed(config) available_and_installed = set(list(available.keys()) + list(installed.keys())) available_not_installed = set(available.keys()) - set(installed.keys()) max_len = max(map(len, available_and_installed)) header = line.format(name='Plugin', width=max_len, version='Version', enabled='Active', status='Status') line_length = max(max_len, len('Plugin')) + len(header) - len('Plugin') - 12 print('-' * line_length) print(header) print('-' * line_length) # only installed (maybe update available?) for plugin, filename in sorted(installed.items()): if not fnmatch(plugin, pattern): continue found = True plugin_info = analyze_plugin(filename) installed_version = parse_version(plugin_info['__version__']) available_version = None if plugin in available: available_plugin_info = analyze_plugin(available[plugin]) available_version = parse_version(available_plugin_info['__version__']) status = "installed" if installed_version and available_version: if available_version > installed_version: status = "installed (^)" enabled = 'enabled' if plugin in config['main']['plugins'] and \ 'enabled' in config['main']['plugins'][plugin] and \ config['main']['plugins'][plugin]['enabled'] \ else 'disabled' print(line.format(name=plugin, width=max_len, version='.'.join(installed_version), enabled=enabled, status=status)) for plugin in sorted(available_not_installed): if not fnmatch(plugin, pattern): continue found = True available_plugin_info = analyze_plugin(available[plugin]) available_version = available_plugin_info['__version__'] print(line.format(name=plugin, width=max_len, version=available_version, enabled='-', status='available')) print('-' * line_length) if not found: logging.info('Maybe try: pwnagotchi plugins update') return 1 return 0
def uninstall(args, config): """ Uninstalls a plugin """ plugin_name = args.name installed = _get_installed(config) if plugin_name not in installed: logging.error('Plugin %s is not installed.', plugin_name) return 1 plugin_info = analyze_plugin(installed[plugin_name]) if '__assets__' in plugin_info: assets = plugin_info['__assets__'] if assets: if not isinstance(assets, list): assets = [assets] for a in assets: for f in glob.glob(a): logging.info('Delete asset: %s', os.path.basename(f)) os.remove(f) os.remove(installed[plugin_name]) # but keep config return 0
def load_from_path(path, enabled=list()): global loaded, database logging.debug("loading plugins from %s - enabled: %s" % (path, enabled)) for filename in glob.glob(os.path.join(path, "*.py")): plugin_name = os.path.basename(filename.replace(".py", "")) if plugin_name == 'example': continue database[plugin_name] = analyze_plugin(filename) if plugin_name in enabled: try: load_from_file(filename) except Exception as e: logging.warning("error while loading %s: %s" % (filename, e)) logging.debug(e, exc_info=True) return loaded
def edit(args, config): """ Edit the config of the plugin """ plugin = args.name editor = os.environ.get('EDITOR', 'vim') # because vim is the best installed = _get_installed(config) if plugin not in config['main']['plugins'] and plugin not in installed: return 1 defaults = dict() if plugin in installed: data = analyze_plugin(installed[plugin]) defaults = data['__defaults__'] if '__defaults__' in data else dict() plugin_config = {'main': {'plugins': {plugin: merge_config(config['main']['plugins'][plugin], defaults)}}} import toml from subprocess import call from tempfile import NamedTemporaryFile from pwnagotchi.utils import DottedTomlEncoder new_plugin_config = None with NamedTemporaryFile(suffix=".tmp", mode='r+t') as tmp: tmp.write(toml.dumps(plugin_config, encoder=DottedTomlEncoder())) tmp.flush() rc = call([editor, tmp.name]) if rc != 0: return rc tmp.seek(0) new_plugin_config = toml.load(tmp) config['main']['plugins'][plugin] = new_plugin_config['main']['plugins'][plugin] save_config(config, args.user_config) return 0
def install(args, config): """ Installs the given plugin """ global DEFAULT_INSTALL_PATH plugin_name = args.name available = _get_available() installed = _get_installed(config) if plugin_name not in available: logging.error('%s not found.', plugin_name) return 1 if plugin_name in installed: logging.error('%s already installed.', plugin_name) # install into custom_plugins path install_path = config['main']['custom_plugins'] if not install_path: install_path = DEFAULT_INSTALL_PATH config['main']['custom_plugins'] = install_path save_config(config, args.user_config) plugin_info = analyze_plugin(available[plugin_name]) if '__min_pwnagotchi_version__' in plugin_info: min_version = parse_version(plugin_info['__min_pwnagotchi_version__']) pwn_version = parse_version(pwnagotchi_version) if pwn_version < min_version: looging.error("Can't install %s because it requires pwnagotchi version %s", plugin_name, min_version) return 1 if '__dependencies__' in plugin_info: deps = plugin_info['__dependencies__'] if 'pip' in deps: for d in deps['pip']: if not pip_install(d): logging.error('Dependency "%s" not found', d) return 1 if 'apt' in deps: for d in deps['apt']: if not apt_install(d): logging.error('Dependency "%s" not found', d) return 1 os.makedirs(install_path, exist_ok=True) if '__assets__' in plugin_info: assets = plugin_info['__assets__'] if assets: if not isinstance(assets, list): assets = [assets] for a in assets: if a.startswith('https://'): dst = os.path.join(install_path, os.path.basename(a)) if not os.path.exists(dst) and not has_internet(): logging.error('Could not download asset %s', a) return 1 logging.info('Downloading asset: %s', os.path.basename(a)) download_file(a, dst) continue for f in glob.glob(a): dst = os.path.join(install_path, os.path.basename(f)) logging.info('Copy asset: %s', os.path.basename(f)) shutil.copyfile(f, dst) shutil.copyfile(available[plugin_name], os.path.join(install_path, os.path.basename(available[plugin_name]))) return 0
def upgrade(args, config, pattern='*'): """ Upgrades the given plugin """ available = _get_available() installed = _get_installed(config) for plugin, filename in installed.items(): if not fnmatch(plugin, pattern) or plugin not in available: continue available_plugin_data = analyze_plugin(available[plugin]) available_version = parse_version(available_plugin_data['__version__']) installed_version = parse_version(analyze_plugin(filename)['__version__']) if installed_version and available_version: if available_version <= installed_version: continue else: continue if '__min_pwnagotchi_version__' in available_plugin_data: min_version = parse_version(available_plugin_data['__min_pwnagotchi_version__']) pwn_version = parse_version(pwnagotchi_version) if pwn_version < min_version: looging.info('Skip %s because it requires pwnagotchi version %s', plugin, min_version) continue logging.info('Upgrade %s from %s to %s', plugin, '.'.join(installed_version), '.'.join(available_version)) if '__dependencies__' in available_plugin_data: deps = available_plugin_data['__dependencies__'] if 'pip' in deps: for d in deps['pip']: if not pip_install(d): logging.error('Dependency "%s" not found', d) return 1 if 'apt' in deps: for d in deps['apt']: if not apt_install(d): logging.error('Dependency "%s" not found', d) return 1 if '__assets__' in available_plugin_data: assets = available_plugin_data['__assets__'] if assets: if not isinstance(assets, list): assets = [assets] for a in assets: if a.startswith('https://'): dst = os.path.join(os.path.dirname(installed[plugin]), os.path.basename(a)) if not os.path.exists(dst) and not has_internet(): logging.error('Could not download asset %s', a) return 1 logging.info('Downloading asset: %s', os.path.basename(a)) download_file(a, dst) continue for f in glob.glob(a): dst = os.path.join(os.path.dirname(installed[plugin]), os.path.basename(f)) logging.info('Copy asset: %s', os.path.basename(f)) shutil.copyfile(f, dst) shutil.copyfile(available[plugin], installed[plugin]) return 0
def list_plugins(args, config, pattern='*'): """ Lists the available and installed plugins """ from rich.console import Console from rich.table import Table console = Console() table = Table(show_header=True, header_style="bold") table.add_column("Name") table.add_column("Version") table.add_column("Enabled") table.add_column("Status") found = False available = _get_available() installed = _get_installed(config) available_not_installed = set(available.keys()) - set(installed.keys()) # only installed (maybe update available?) for plugin, filename in sorted(installed.items()): if not fnmatch(plugin, pattern): continue found = True plugin_info = analyze_plugin(filename) installed_version = parse_version(plugin_info['__version__']) available_version = None if plugin in available: available_plugin_info = analyze_plugin(available[plugin]) available_version = parse_version(available_plugin_info['__version__']) status = "installed" if installed_version and available_version: if available_version > installed_version: status = "installed (^)" enabled = 'enabled' if plugin in config['main']['plugins'] and \ 'enabled' in config['main']['plugins'][plugin] and \ config['main']['plugins'][plugin]['enabled'] \ else 'disabled' table.add_row(plugin, '.'.join(installed_version), enabled, status) for plugin in sorted(available_not_installed): if not fnmatch(plugin, pattern): continue found = True available_plugin_info = analyze_plugin(available[plugin]) available_version = available_plugin_info['__version__'] table.add_row(plugin, available_version, '-', 'available') if not found: logging.info('Maybe try: pwnagotchi plugins update') return 1 console.print(table) return 0