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 _extract_version(filename): """ Extracts the version from a python file """ plugin_content = open(filename, 'rt').read() m = re.search(r'__version__[\t ]*=[\t ]*[\'\"]([^\"\']+)', plugin_content) if m: return parse_version(m.groups()[0]) return None
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