diff --git a/TODO b/TODO index ec0678d..8e7d1a1 100644 --- a/TODO +++ b/TODO @@ -1,28 +1,24 @@ - write classes/functions - XML-based config +-x XML syntax +--- xregex btags - case-insensitive? this can be represented in-pattern: + xhttps://stackoverflow.com/a/9655186/733214 +-x configuration generator +--- xprint end result xml config to stderr for easier redirection? or print prompts to stderr and xml to stdout? +-- xXSD for validation +-- Flask app for generating config? +-- TKinter (or pygame?) GUI? +--- https://docs.python.org/3/faq/gui.html +--- https://www.pygame.org/wiki/gui - ensure we use docstrings in a Sphinx-compatible manner? https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html at the very least document all the functions and such so pydoc's happy. -- better prompt display. i might include them as constants in a separate file - and then import it for e.g. confgen. or maybe a Flask website/app? + - locking - for docs, 3.x (as of 3.10) was 2.4M. -- GUI? at least for generating config... -- Need ability to write/parse mtree specs (or a similar equivalent) for applying ownerships/permissions to overlay files +- xNeed ability to write/parse mtree specs (or a similar equivalent) for applying ownerships/permissions to overlay files +-- parsing is done. writing may? come later. -- SSL key gen: -import OpenSSL -k = OpenSSL.crypto.PKey() -k.generate_key(OpenSSL.crypto.TYPE_RSA, 4096) -x = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, - k, - cipher = 'aes256', - passphrase = 'test') - -- need to package: - python-hashid (https://psypanda.github.io/hashID/, - https://github.com/psypanda/hashID, - https://pypi.org/project/hashID/) - package for PyPI: # https://packaging.python.org/tutorials/distributing-packages/ @@ -38,7 +34,6 @@ BUGS.SQUARE-R00T.NET bugs/tasks: #14: Use os.path.join() for more consistency/pythonicness #24: Run as regular user? (pychroot? fakeroot?) #34: Build-time support for only building single phase of build -#36: Allow parsing pkg lists with inline comments #39: Fix UEFI #40: ISO overlay (to add e.g. memtest86+ to final ISO) -#43: Support resuming partial tarball downloads (Accet-Ranges: bytes) \ No newline at end of file +#43: Support resuming partial tarball downloads (Accept-Ranges: bytes) diff --git a/bdisk/BIOS.py b/bdisk/BIOS.py index 012fb51..cd314bb 100644 --- a/bdisk/BIOS.py +++ b/bdisk/BIOS.py @@ -1,3 +1,4 @@ import jinja2 import os import shutil + diff --git a/bdisk/GPG.py b/bdisk/GPG.py index 202b62e..ad10df8 100644 --- a/bdisk/GPG.py +++ b/bdisk/GPG.py @@ -1,8 +1,26 @@ +import datetime import gpg import os import psutil import gpg.errors + +# This helps translate the input name from the conf to a string compatible with the gpg module. +_algmaps = {#'cv': 'cv{keysize}', # DISABLED, can't sign (only encrypt). Currently only 25519 + 'ed': 'ed{keysize}', # Currently only 25519 + #'elg': 'elg{}', # DISABLED, can't sign (only encrypt). 1024, 2048, 4096 + 'nist': 'nistp{keysize}', # 256, 384, 521 + 'brainpool.1': 'brainpoolP{keysize}r1', # 256, 384, 512 + 'sec.k1': 'secp{keysize}k1', # Currently only 256 + 'rsa': 'rsa{keysize}', # Variable (1024 <> 4096), but we only support 1024, 2048, 4096 + 'dsa': 'dsa{keysize}'} # Variable (768 <> 3072), but we only support 768, 2048, 3072 + +# This is just a helper function to get a delta from a unix epoch. +def _epoch_helper(epoch): + d = datetime.datetime.utcfromtimestamp(epoch) - datetime.datetime.utcnow() + return(abs(int(d.total_seconds()))) # Returns a positive integer even if negative... + #return(int(d.total_seconds())) + # http://files.au.adversary.org/crypto/GPGMEpythonHOWTOen.html # https://www.gnupg.org/documentation/manuals/gpgme.pdf # Support ECC? https://www.gnupg.org/faq/whats-new-in-2.1.html#ecc @@ -60,7 +78,7 @@ class GPGHandler(object): self._prep_home() else: self._check_home() - self.ctx = self.get_context(home_dir = self.home) + self.ctx = self.GetContext(home_dir = self.home) def _check_home(self, home = None): if not home: @@ -94,11 +112,12 @@ class GPGHandler(object): 'write to') return() - def get_context(self, **kwargs): + def GetContext(self, **kwargs): ctx = gpg.Context(**kwargs) return(ctx) - def kill_stale_agent(self): + def KillStaleAgent(self): + # Is this even necessary since I switched to the native gpg module instead of the gpgme one? _process_list = [] # TODO: optimize; can I search by proc name? for p in psutil.process_iter(): @@ -113,7 +132,64 @@ class GPGHandler(object): # for p in plst: # psutil.Process(p).terminate() - def get_sigs(self, data_in): + def CreateKey(self, name, algo, keysize, email = None, comment = None, passwd = None, key = None, expiry = None): + algo = _algmaps[algo].format(keysize = keysize) + userid = name + userid += ' ({0})'.format(comment) if comment else '' + userid += ' <{0}>'.format(email) if email else '' + if not expiry: + expires = False + else: + expires = True + self.ctx.create_key(userid, + algorithm = algo, + expires = expires, + expires_in = _epoch_helper(expiry), + sign = True) + # Even if expires is False, it still parses the expiry... + # except OverflowError: # Only trips if expires is True and a negative expires occurred. + # raise ValueError(('Expiration epoch must be 0 (to disable) or a future time! ' + # 'The specified epoch ({0}, {1}) is in the past ' + # '(current time is {2}, {3}).').format(expiry, + # str(datetime.datetime.utcfromtimestamp(expiry)), + # datetime.datetime.utcnow().timestamp(), + # str(datetime.datetime.utcnow()))) + return(k) + # We can't use self.ctx.create_key; it's a little limiting. + # It's a fairly thin wrapper to .op_createkey() (the C GPGME API gpgme_op_createkey) anyways. + flags = (gpg.constants.create.SIGN | + gpg.constants.create.CERT) + if not expiry: + flags = (flags | gpg.constants.create.NOEXPIRE) + if not passwd: + flags = (flags | gpg.constants.create.NOPASSWD) + else: + # Thanks, gpg/core.py#Context.create_key()! + sys_pinentry = gpg.constants.PINENTRY_MODE_DEFAULT + old_pass_cb = getattr(self, '_passphrase_cb', None) + self.ctx.pinentry_mode = gpg.constants.PINENTRY_MODE_LOOPBACK + def passphrase_cb(hint, desc, prev_bad, hook = None): + return(passwd) + self.ctx.set_passphrase_cb(passphrase_cb) + try: + if not key: + try: + self.ctx.op_createkey(userid, algo, 0, 0, flags) + k = self.ctx.get_key(self.ctx.op_genkey_result().fpr, secret = True) + else: + if not isinstance(key, gpg.gpgme._gpgme_key): + key = self.ctx.get_key(key) + if not key: + raise ValueError('Key {0} does not exist'.format()) + #self.ctx.op_createsubkey(key, ) + finally: + if not passwd: + self.ctx.pinentry_mode = sys_pinentry + if old_pass_cb: + self.ctx.set_passphrase_cb(*old_pass_cb[1:]) + return(k) + + def GetSigs(self, data_in): key_ids = [] # Currently as of May 13, 2018 there's no way using the GPGME API to do # the equivalent of the CLI's --list-packets. @@ -131,3 +207,9 @@ class GPGHandler(object): l = [i.strip() for i in line.split(':')] key_ids.append(l[0]) return(key_ids) + + def CheckSigs(self, keys, sig_data): + try: + self.ctx.verify(sig_data) + except: + pass # TODO diff --git a/bdisk/SSL.py b/bdisk/SSL.py index a991649..0c91e27 100644 --- a/bdisk/SSL.py +++ b/bdisk/SSL.py @@ -2,4 +2,11 @@ import OpenSSL # https://cryptography.io/en/latest/x509/reference/#cryptography.x509.CertificateBuilder.sign # migrate old functions of bSSL to use cryptography # but still waiting on their recpipes. -# https://cryptography.io/en/latest/x509/tutorial/ \ No newline at end of file +# https://cryptography.io/en/latest/x509/tutorial/ +#import OpenSSL +#k = OpenSSL.crypto.PKey() +#k.generate_key(OpenSSL.crypto.TYPE_RSA, 4096) +#x = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, +# k, +# cipher = 'aes256', +# passphrase = 'test') \ No newline at end of file diff --git a/bdisk/basedistro/antergos.py b/bdisk/basedistro/antergos.py new file mode 120000 index 0000000..3d58414 --- /dev/null +++ b/bdisk/basedistro/antergos.py @@ -0,0 +1 @@ +archlinux.py \ No newline at end of file diff --git a/bdisk/basedistro/arch.py b/bdisk/basedistro/arch.py new file mode 120000 index 0000000..3d58414 --- /dev/null +++ b/bdisk/basedistro/arch.py @@ -0,0 +1 @@ +archlinux.py \ No newline at end of file diff --git a/bdisk/basedistro/archlinux.py b/bdisk/basedistro/archlinux.py new file mode 100644 index 0000000..c57f006 --- /dev/null +++ b/bdisk/basedistro/archlinux.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +# Supported initsys values: +# systemd +# Possible future inclusions: +# openrc +# runit +# sinit +# s6 +# shepherd +initsys = 'systemd' + +def extern_prep(cfg, cur_arch = 'x86_64'): + import os + import re + mirrorlist = os.path.join(cfg['build']['paths']['chroot'], + cur_arch, + 'etc/pacman.d/mirrorlist') + with open(mirrorlist, 'r') as f: + mirrors = [] + for i in f.readlines(): + m = re.sub('^\s*#.*$', '', i.strip()) + if m != '': + mirrors.append(m) + if not mirrors: + # We do this as a fail-safe. + mirror = ('\n\n# Added by BDisk\n' + 'Server = https://arch.mirror.square-r00t.net/' + '$repo/os/$arch\n') + with open(mirrorlist, 'a') as f: + f.write(mirror) + return() + +# This will be run before the regular packages are installed. It can be +# whatever script you like, as long as it has the proper shebang and doesn't +# need additional packages installed. +# In Arch's case, we use it for initializing the keyring and installing an AUR +# helper. +pkg_mgr_prep = """#!/bin/bash + +pacman -Syy +pacman-key --init +pacman-key --populate archlinux +pacman -S --noconfirm --needed base +pacman -S --noconfirm --needed base-devel multilib-devel git linux-headers \ + mercurial subversion vala xorg-server-devel +cd /tmp +sqrt="https://git.square-r00t.net/BDisk/plain/external" +# Temporary until there's another AUR helper that allows dropping privs AND +# automatically importing GPG keys. +pkg="${sqrt}/apacman-current.pkg.tar.xz?h=4.x_rewrite" +curl -sL -o apacman-current.pkg.tar.xz ${pkg} +pacman -U --noconfirm apacman-current.pkg.tar.xz +rm apacman* +""" + +# Special values: +# {PACKAGE} = the package name +# {VERSION} = the version specified in the attribute +# {REPO} = the repository specified in the attribute +# If check_cmds are needed to run before installing, set pre_check to True. +# Return code 0 means the package is installed already, anything else means we +# should try to install it. +#### AUR SUPPORT #### +packager = {'pre_check': False, + 'sys_update': ['/usr/bin/apacman', '-S', '-u'], + 'sync_cmd': ['/usr/bin/apacman', '-S', '-y', '-y'], + 'check_cmds': {'versioned': ['/usr/bin/pacman', + '-Q', '-s', + '{PACKAGE}'], + 'unversioned': ['/usr/bin/pacman', + '-Q', '-s', + '{PACKAGE}'] + }, + 'update_cmds': {'versioned': ['/usr/bin/pacman', + '-S', '-u', + '{PACKAGE}'], + 'unversioned': ['/usr/bin/pacman', + '-S', '-u', + '{PACKAGE}'] + }, + } + +# These are packages *required* to exist on the base guest, no questions asked. +# TODO: can this be trimmed down? +prereqs = ['arch-install-scripts', 'archiso', 'bzip2', 'coreutils', + 'customizepkg-scripting', 'cronie', 'dhclient', 'dhcp', 'dhcpcd', + 'dosfstools', 'dropbear', 'efibootmgr', 'efitools', 'efivar', + 'file', 'findutils', 'iproute2', 'iputils', 'libisoburn', + 'localepurge', 'lz4', 'lzo', 'lzop', 'mkinitcpio-nbd', + 'mkinitcpio-nfs-utils', 'mkinitcpio-utils', 'nbd', 'ms-sys', + 'mtools', 'net-tools', 'netctl', 'networkmanager', 'pv', + 'python', 'python-pyroute2', 'rsync', 'sed', 'shorewall', + 'squashfs-tools', 'sudo', 'sysfsutils', + 'syslinux', 'traceroute', 'vi'] + diff --git a/bdisk/basedistro/manjaro.py b/bdisk/basedistro/manjaro.py new file mode 120000 index 0000000..3d58414 --- /dev/null +++ b/bdisk/basedistro/manjaro.py @@ -0,0 +1 @@ +archlinux.py \ No newline at end of file diff --git a/bdisk/bdisk.xsd b/bdisk/bdisk.xsd index 2e4f92c..4f7ab91 100644 --- a/bdisk/bdisk.xsd +++ b/bdisk/bdisk.xsd @@ -1,6 +1,933 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bdisk/confgen.py b/bdisk/confgen.py index cb9642d..a321ed4 100755 --- a/bdisk/confgen.py +++ b/bdisk/confgen.py @@ -1,10 +1,10 @@ -#!/usr/bin/env python3.6 +#!/usr/bin/env python3 # Ironically enough, I think building a GUI for this would be *cleaner*. # Go figure. import confparse -import crypt +import datetime import getpass import os import utils @@ -134,7 +134,12 @@ class ConfGenerator(object): self.cfg = c.xml self.append = True else: - self.cfg = lxml.etree.Element('bdisk') + _ns = {None: 'http://bdisk.square-r00t.net/', + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance'} + _xsi = { + '{http://www.w3.org/2001/XMLSchema-instance}schemaLocation': + 'http://bdisk.square-r00t.net bdisk.xsd'} + self.cfg = lxml.etree.Element('bdisk', nsmap = _ns, attrib = _xsi) self.append = False self.profile = lxml.etree.Element('profile') self.cfg.append(self.profile) @@ -155,6 +160,13 @@ class ConfGenerator(object): self.get_pki() self.get_gpg() self.get_sync() + # TODO: make this more specific (script? gui? web? etc.) + # and append comment to bdisk element + _comment = lxml.etree.Comment( + 'Generated {0} by BDisk configuration generator'.format( + str(datetime.datetime.now()) + ) + ) except KeyboardInterrupt: exit('\n\nCaught KeyboardInterrupt; quitting...') return() @@ -442,6 +454,8 @@ class ConfGenerator(object): tarball_elem.attrib['flags'] = 'latest' tarball_elem.text = tarball['full_url'] print('\n++ SOURCES || {0} || CHECKSUM ++'.format(arch.upper())) + # TODO: explicit not being set for explicitly-set sums, + # and checksum string not actually added to those. investigate. chksum = lxml.etree.SubElement(source, 'checksum') _chksum_chk = prompt.confirm_or_no(prompt = ( '\nWould you like to add a checksum for the tarball? (BDisk ' @@ -470,7 +484,7 @@ class ConfGenerator(object): print('Invalid selection. Starting over.') continue chksum.attrib['hash_algo'] = checksum_type - chksum.attrib['explicit'] = "no" + chksum.attrib['explicit'] = "false" chksum.text = checksum['full_url'] else: # Maybe it's a digest string. @@ -502,8 +516,8 @@ class ConfGenerator(object): print('Invalid selection. Starting over.') continue else: - checksum_type == checksum_type[0] - chksum.attrib['explicit'] = "yes" + checksum_type = checksum_type[0] + chksum.attrib['explicit'] = "true" chksum.text = checksum chksum.attrib['hash_algo'] = checksum_type print('\n++ SOURCES || {0} || GPG ++'.format(arch.upper())) @@ -595,7 +609,7 @@ class ConfGenerator(object): usage = ( '{0} for yes, {1} for no...\n')) if _chk_optimizations: - build.attrib['its_full_of_stars'] = 'yes' + build.attrib['its_full_of_stars'] = 'true' print('\n++ BUILD || PATHS ++') # Thankfully, we can simplify a lot of this. _dir_strings = {'base': ('the base directory (used for files that are ' @@ -625,7 +639,7 @@ class ConfGenerator(object): 'created that can be used to serve iPXE)'), 'tftp': ('the TFTP directory (where a TFTP/' 'traditional PXE root is created)'), - 'ssl': ('the SSL/TLS PKI directory (where we store ' + 'pki': ('the SSL/TLS PKI directory (where we store ' 'the PKI structure we use/re-use - MAKE SURE ' 'it is in a path that is well-protected!)')} has_paths = False @@ -676,9 +690,9 @@ class ConfGenerator(object): self.profile.append(iso) # We have more than one arch, so we need to ask how they want to handle # it. - _ma_strings = {'yes': ('a multi-arch ISO (both architectures on one ' + _ma_strings = {'true': ('a multi-arch ISO (both architectures on one ' 'ISO)'), - 'no': ('separate image files for ' + 'false': ('separate image files for ' '{0}').format(' and '.join(_arches))} for a in _arches: _ma_strings[a] = 'only build an image file for {0}'.format(a) @@ -710,7 +724,7 @@ class ConfGenerator(object): 'option to configure it a bit later).\nWould you like to sign ' 'the ISO/USB image files with GPG?\n'), usage = ( '{0} for yes, {1} for no...\n')) - _gpg_sign = ('yes' if _gpg_input else 'no') + _gpg_sign = ('true' if _gpg_input else 'false') iso.attrib['sign'] = _gpg_sign self.profile.append(iso) return() @@ -725,21 +739,21 @@ class ConfGenerator(object): 'see the manual for more information). Would you like to ' 'build iPXE support?\n'), usage = ( '{0} for yes, {1} for no...\n')) - _ipxe = ('yes' if _ipxe else 'no') - if _ipxe == 'yes': + _ipxe = ('true' if _ipxe else 'true') + if _ipxe == 'true': print('\n++ iPXE || MINI-ISO ++') _iso = prompt.confirm_or_no(prompt = ( '\nWould you like to build a "mini-ISO" (see the manual) for ' 'bootstrapping iPXE booting from USB or optical media?\n'), usage = ('{0} for yes, {1} for no...\n')) - ipxe.attrib['iso'] = ('yes' if _iso else 'no') + ipxe.attrib['iso'] = ('true' if _iso else 'false') print('\n++ iPXE || SIGNING ++') _sign = prompt.confirm_or_no(prompt = ( '\nBDisk can sign the mini-ISO and other relevant files for ' 'iPXE builds using GPG. Would you like to sign the iPXE build ' 'distributables? (You\'ll have the chance to configure GPG ' 'later).\n'), usage = ('{0} for yes, {1} for no...\n')) - ipxe.attrib['sign'] = ('yes' if _sign else 'no') + ipxe.attrib['sign'] = ('true' if _sign else 'false') _uri = None while not _uri: print('\n++ iPXE || URL ++') @@ -754,7 +768,7 @@ class ConfGenerator(object): else: uri = lxml.etree.SubElement(ipxe, 'uri') uri.text = _uri - if _ipxe == 'yes': + if _ipxe == 'true': self.profile.append(ipxe) return() @@ -780,7 +794,7 @@ class ConfGenerator(object): 'wish to keep persistent keys and certs), you should ' 'DEFINITELY answer no here.\n'), usage = ('{0} for yes, {1} for no...\n')) - pki.attrib['overwrite'] = ('yes' if _overwrite else 'no') + pki.attrib['overwrite'] = ('true' if _overwrite else 'false') for x in ('ca', 'client'): print('\n++ SSL/TLS PKI || {0} ++'.format(x.upper())) _x = None @@ -804,7 +818,7 @@ class ConfGenerator(object): for x in _xpaths: _x = self.profile.xpath(x) for a in _x: - if a == 'yes': + if a == 'true': _sigchk = True break if _sigchk: @@ -848,7 +862,7 @@ class ConfGenerator(object): '\nWould you like to push the key to the SKS keyserver pool ' '(making it much easier for end-users to look it up)?\n'), usage = ('{0} for yes, {1} for no...\n')) - gpg.attrib['publish'] = ('yes' if _gpgpublish else 'no') + gpg.attrib['publish'] = ('true' if _gpgpublish else 'false') print('\n++ GPG || PASSWORD HANDLING ++') _gpgpass_prompt = prompt.confirm_or_no(prompt = ( '\nWould you like BDisk to prompt you for a passphrase? If not, ' @@ -856,7 +870,8 @@ class ConfGenerator(object): 'the configuration (HIGHLY unrecommended) or use a blank ' 'passphrase (also HIGHLY unrecommended).\n'), usage = ('{0} for yes, {1} for no...\n')) - gpg.attrib['prompt_passphrase'] = ('yes' if _gpgpass_prompt else 'no') + gpg.attrib['prompt_passphrase'] = ('true' if _gpgpass_prompt else + 'false') _pass = None if not _gpgpass_prompt: while not _pass: @@ -921,7 +936,7 @@ class ConfGenerator(object): '\nWould you like to sync {0}?\n'.format(_syncs[s])), usage = ('{0} for yes, {1} for no...\n')) elem = lxml.etree.SubElement(sync, s) - elem.attrib['enabled'] = ('yes' if _item_sync_chk else 'no') + elem.attrib['enabled'] = ('true' if _item_sync_chk else 'false') if not _item_sync_chk: continue if s == 'gpg': @@ -935,12 +950,12 @@ class ConfGenerator(object): '\n\t'.join(_choices) ))).strip().lower() if _export_type.startswith('a'): - _export_type == 'asc' + _export_type = 'asc' elif _export_type.startswith('b'): - _export_type == 'bin' + _export_type = 'bin' else: print('Using the default.') - _export_type == 'asc' + _export_type = 'asc' elem.attrib['format'] = _export_type _path = None while not _path: diff --git a/bdisk/confparse.py b/bdisk/confparse.py index 2cc271e..56abec9 100644 --- a/bdisk/confparse.py +++ b/bdisk/confparse.py @@ -1,15 +1,20 @@ import copy -import re import os +import pprint +import re import utils -import validators import lxml.etree from urllib.parse import urlparse etree = lxml.etree +detect = utils.detect() +generate = utils.generate() +transform = utils.transform() +valid = utils.valid() class Conf(object): - def __init__(self, cfg, profile = None): + def __init__(self, cfg, profile = None, validate_cfg = False, + xsd_file = None): """ A configuration object. @@ -36,92 +41,351 @@ class Conf(object): You can provide any combination of these (e.g. "profile={'id': 2, 'name' = 'some_profile'}"). + Non-greedy matching (meaning ALL attributes specified + must match). """ - #self.raw = _detect_cfg(cfg) # no longer needed; in utils + if validate_cfg == 'pre': + # Validate before attempting any other operations + self.validate() self.xml_suppl = utils.xml_supplicant(cfg, profile = profile) - self.profile = self.xml_suppl - self.xml = None - self.profile = None - # Mad props to https://stackoverflow.com/a/12728199/733214 - self.xpath_re = re.compile('(?<=(? 0: + _hash.update(_hashbuf) + _hashbuf = f.read(64000) + if _hash.hexdigest().lower() != _source['checksum']['value'].lower(): + return(False) + return(True) + def _sig_verify(gpg_instance): # TODO: move to utils.valid()? or just use as part of the bdisk.GPG module? + pass + if os.path.isfile(_tarball): + download = _hash_verify() + download = _sig_verify() + if download: + d = utils.Download(_remote_tarball) diff --git a/bdisk/main.py b/bdisk/main.py index 40af6a6..9a54f67 100644 --- a/bdisk/main.py +++ b/bdisk/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3.6 +#!/usr/bin/env python3 import argparse import confparse @@ -14,8 +14,10 @@ def parseArgs(): epilog = ('https://git.square-r00t.net')) return(args) -def run(): - pass +def run(cfg): + cfg = confparse.Conf(cfg, validate_cfg = True) + cfg.parse_all() + def run_interactive(): args = vars(parseArgs().parse_args()) diff --git a/bdisk/mtree.py b/bdisk/mtree.py new file mode 100755 index 0000000..b74b730 --- /dev/null +++ b/bdisk/mtree.py @@ -0,0 +1,396 @@ +#!/usr/bin/env python3 + +import argparse +import copy +import datetime +import grp +import hashlib +import os +import pathlib +import platform +import pwd +import re +import stat +from collections import OrderedDict +try: + import pycksum + has_cksum = True +except ImportError: + has_cksum = False + +# Parse BSD mtree spec files. +# On arch, BSD mtree is ported in the AUR as nmtree. +# TODO: add a generator class as well? (in process) +# TODO: add a checking function as well? + +# The format used for headers +_header_strptime_fmt = '%a %b %d %H:%M:%S %Y' + +# Supported hash types (for generation). These are globally available always. +_hashtypes = ['md5', 'sha1', 'sha256', 'sha384', 'sha512'] +# If RIPEMD-160 is supported, we add it (after MD5). +if 'ripemd160' in hashlib.algorithms_available: + _hashtypes.insert(1, 'rmd160') + +# Iterative to determine which type an item is. +_stype_map = {'block': stat.S_ISBLK, + 'char': stat.S_ISCHR, + 'dir': stat.S_ISDIR, + 'fifo': stat.S_ISFIFO, + 'file': stat.S_ISREG, + 'link': stat.S_ISLNK, + 'socket': stat.S_ISSOCK} + +# Regex pattern for cleaning up an octal perm mode into a string representation. +_octre = re.compile('^0o') + +class MTreeGen(object): + def __init__(self, path): + self.path = pathlib.PosixPath(os.path.abspath(os.path.expanduser(path))) + # These are used to keep a cached copy of the info. + self._sysinfo = {'uids': {}, 'gids': {}} + self._build_header() + # We use this to keep track of where we are exactly in the tree so we can generate a full absolute path at + # any moment relative to the tree. + self._path_pointer = copy.deepcopy(self.path) + + + def paths_iterator(self): + for root, dirs, files in os.walk(self.path): + for f in files: + _fname = self.path.joinpath(f) + _stats = self._get_stats(_fname) + if not _stats: + print(('WARNING: {0} either disappeared while we were trying to parse it or ' + 'it is a broken symlink.').format(_fname)) + continue + # TODO: get /set line here? + item = ' {0} \\\n'.format(f) + _type = 'file' # TODO: stat this more accurately + _cksum = self._gen_cksum(_fname) + item += ' {0} {1} {2}\\\n'.format(_stats['size'], + _stats['time'], + ('{0} '.format(_cksum) if _cksum else '')) + # TODO: here's where the hashes would get added + # TODO: here's where we parse dirs. maybe do that before files? + # remember: mtree specs use ..'s to traverse upwards when done with a dir + for d in dirs: + _dname = self.path.joinpath(d) + _stats = self._get_stats(_dname) + if not _stats: + print(('WARNING: {0} either disappeared while we were trying to parse it or ' + 'it is a broken symlink.').format(_dname)) + continue + # TODO: get /set line here? + return() + + + def _gen_cksum(self, fpath): + if not has_cksum: + return(None) + if not os.path.isfile(fpath): + return(None) + # TODO: waiting on https://github.com/sobotklp/pycksum/issues/2 for byte iteration (because large files maybe?) + c = pycksum.Cksum() + with open(fpath, 'rb') as f: + c.add(f) + return(c.get_cksum()) + + + def _get_stats(self, path): + stats = {} + try: + _st = os.stat(path, follow_symlinks = False) + except FileNotFoundError: + # Broken symlink? Shouldn't occur since follow_symlinks is False anyways, BUT... + return(None) + # Ownership + stats['uid'] = _st.st_uid + stats['gid'] = _st.st_gid + if _st.st_uid in self._sysinfo['uids']: + stats['uname'] = self._sysinfo['uids'][_st.st_uid] + else: + _pw = pwd.getpwuid(_st.st_uid).pw_name + stats['uname'] = _pw + self._sysinfo['uids'][_st.stuid] = _pw + if _st.st_gid in self._sysinfo['gids']: + stats['gname'] = self._sysinfo['gids'][_st.st_gid] + else: + _grp = grp.getgrgid(_st.st_gid).gr_name + stats['gname'] = _grp + self._sysinfo['gids'][_st.stgid] = _grp + # Type and Mode + for t in _stype_map: + if _stype_map[t](_st.st_mode): + stats['type'] = t + # TODO: need a reliable way of parsing this. + # for instance, for /dev/autofs, _st.st_dev = 6 (os.makedev(6) confirms major is 0, minor is 6) + # but netBSD mtree (ported) says it's "0xaeb" (2795? or, as str, "®b" apparently). + # I'm guessing the kernel determines this, but where is it pulling it from/how? + # We can probably do 'format,major,minor' (or, for above, 'linux,0,6'). + # if t in ('block', 'char'): + # stats['device'] = None + # Handle symlinks. + if t == 'link': + _target = path + while os.path.islink(_target): + _target = os.path.realpath(_target) + stats['link'] = _target + break + stats['mode'] = '{0:0>4}'.format(_octre.sub('', str(oct(stat.S_IMODE(_st.st_mode))))) + stats['size'] = _st.st_size + stats['time'] = str(float(_st.st_mtime)) + stats['nlink'] = _st.st_nlink + # TODO: "flags" keyword? is that meaningful on linux? + stats['flags'] = 'none' + return(stats) + + + + def _gen_hashes(self, fpath): + hashes = OrderedDict({}) + if not os.path.isfile(fpath): + return(hashes) + _hashnums = len(_hashtypes) + for idx, h in enumerate(_hashtypes): + # Stupid naming inconsistencies. + _hashname = (h if h is not 'rmd160' else 'ripemd160') + _hasher = hashlib.new(_hashname) + with open(fpath, 'rb') as f: + # Hash 64kb at a time in case it's a huge file. TODO: is this the most ideal chunk size? + _hashbuf = f.read(64000) + while len(_hashbuf) > 0: + _hasher.update(_hashbuf) + _hashbuf = f.read(64000) + hashes[h] = _hasher.hexdigest() + return(hashes) + # if idx + 1 < _hashnums: + # hashes += ' {0}={1} \\\n'.format(h, _hasher.hexdigest()) + # else: + # hashes += ' {0}={1}\n'.format(h, _hasher.hexdigest()) + # return(hashes) + + + def _build_header(self): + self.spec = '' + _header = OrderedDict({}) + _header['user'] = pwd.getpwuid(os.geteuid()).pw_name + _header['machine'] = platform.node() + _header['tree'] = str(self.path) + _header['date'] = datetime.datetime.utcnow().strftime(_header_strptime_fmt) + for h in _header: + self.spec += '#\t{0:>7}: {1}\n'.format(h, _header[h]) + self.spec += '\n' + return() + + + +class MTreeParse(object): + def __init__(self, spec): + if not isinstance(spec, (str, bytes)): + raise ValueError('spec must be a raw string of the spec or a bytes object of the string') + if isinstance(spec, bytes): + try: + spec = spec.decode('utf-8') + except UnicodeDecodeError: + raise ValueError('spec must be a utf-8 encoded set of bytes if using byte mode') + self.orig_spec = copy.deepcopy(spec) # For referencing in case someone wanted to write it out. + # We NOW need to handle the escaped linebreaking it does. + self._specdata = re.sub('\\\\\s+', '', spec).splitlines() + self._get_header() + self.spec = {'header': self.header, + 'paths': {}} + # Template for an item. + # Default keywords are: + # flags, gid, link, mode, nlink, size, time, type, uid + self._tplitem = { + 'type': None, # ('block', 'char', 'dir', 'fifo', 'file', 'link', 'socket') + # checksum of file (if it's a file) (int) + # On all *nix platforms, the cksum(1) utility (which is what the mtree spec uses) follows + # the POSIX standard CRC (which is NOT CRC-1/CRC-16 nor CRC32!): + # http://pubs.opengroup.org/onlinepubs/009695299/utilities/cksum.html + # For a python implementation, + # https://stackoverflow.com/questions/6835381/python-equivalent-of-unix-cksum-function + # See also crcmod (in PyPi). + 'cksum': None, + # "The device number to use for block or char file types." Should be converted to a tuple of one + # of the following: + # - (format(str), major(int), minor(int)) + # - (format(str), major(int), unit(str?), subunit(str?)) (only used on bsdos formats) + # - (number(int?), ) ("opaque" number) + # Valid formats are, per man page of mtree: + # native, 386bsd, 4bsd, bsdos, freebsd, hpux, isc, linux, netbsd, osf1, sco, solaris, sunos, + # svr3, svr4, ultrix + 'device': None, + # File flags as symbolic name. BSD-specific thing? TODO: testing on BSD system + 'flags': [], + 'ignore': False, # An mtree-internal flag to ignore hierarchy under this item + 'gid': None, # The group ID (int) + 'gname': None, # The group name (str) + 'link': None, # The link target/source, if a link. + # The MD5 checksum digest (str? hex?). "md5digest" is a synonym for this, so it's consolidated in + # as the same keyword. + 'md5': None, + # The mode (in octal) (we convert it to a python-native int for os.chmod/stat, etc.) + # May also be a symbolic value; TODO: map symbolic to octal/int. + 'mode': None, + 'nlink': None, # Number of hard links for this item. + 'optional': False, # This item may or may not be present in the compared directory for checking. + 'rmd160': None, # The RMD-160 checksum of the file. "rmd160digest" is a synonym. + 'sha1': None, # The SHA-1 sum. "sha1digest" is a synonym. + 'sha256': None, # SHA-2 256-bit checksum; "sha256digest" is a synonym. + 'sha384': None, # SHA-2 384-bit checksum; "sha384digest" is a synonym. + 'sha512': None, # SHA-2 512-bit checksum; "sha512digest" is a synonym. + 'size': None, # Size of the file in bytes (int). + 'tags': [], # mtree-internal tags (comma-separated in the mtree spec). + 'time': None, # Time the file was last modified (in Epoch fmt as float). + 'uid': None, # File owner UID (int) + 'uname': None # File owner username (str) + # And lastly, "children" is where the children files/directories go. We don't include it in the template; + # it's added programmatically. + # 'children': {} + } + # Global aspects are handled by "/set" directives. + # They are restored by an "/unset". Since they're global and stateful, they're handled as a class attribute. + self.settings = copy.deepcopy(self._tplitem) + self._parse_items() + del(self.settings, self._tplitem) + + + def _get_header(self): + self.header = {} + _headre = re.compile('^#\s+(user|machine|tree|date):\s') + _cmtre = re.compile('^\s*#\s*') + _blklnre = re.compile('^\s*$') + for idx, line in enumerate(self._specdata): + if _headre.search(line): # We found a header item. + l = [i.lstrip() for i in _cmtre.sub('', line).split(':', 1)] + header = l[0] + val = (l[1] if l[1] is not '(null)' else None) + if header == 'date': + val = datetime.datetime.strptime(val, _header_strptime_fmt) + elif header == 'tree': + val = pathlib.PosixPath(val) + self.header[header] = val + elif _blklnre.search(line): + break # We've reached the end of the header. Otherwise... + else: # We definitely shouldn't be here, but this means the spec doesn't even have a header. + break + return() + + + def _parse_items(self): + # A pattern (compiled for performance) to match commands. + _stngsre = re.compile('^/(un)?set\s') + # Per the man page: + # "Empty lines and lines whose first non-whitespace character is a hash mark (‘#’) are ignored." + _ignre = re.compile('^(\s*(#.*)?)?$') + # The following regex is used to quickly and efficiently check for a synonymized hash name. + _hashre = re.compile('^(md5|rmd160|sha1|sha256|sha384|sha512)(digest)?$') + # The following regex is to test if we need to traverse upwards in the path. + _parentre = re.compile('^\.{,2}/?$') + # _curpath = self.header['tree'] + _curpath = pathlib.PosixPath('/') + _types = ('block', 'char', 'dir', 'fifo', 'file', 'link', 'socket') + # This parses keywords. Used by both item specs and /set. + def _kwparse(kwline): + out = {} + for i in kwline: + l = i.split('=', 1) + if len(l) < 2: + l.append(None) + k, v = l + if v == 'none': + v = None + # These are represented as octals. + if k in ('mode', ): + # TODO: handle symbolic references too (e.g. rwxrwxrwx) + if v.isdigit(): + v = int(v, 8) # Convert from the octal. This can then be used directly with os.chmod etc. + # These are represented as ints + elif k in ('uid', 'gid', 'cksum', 'nlink'): + if v.isdigit(): + v = int(v) + # These are booleans (represented as True by their presence). + elif k in ('ignore', 'optional'): + v = True + # These are lists (comma-separated). + elif k in ('flags', 'tags'): + if v: + v = [i.strip() for i in v.split(',')] + # The following are synonyms. + elif _hashre.search(k): + k = _hashre.sub('\g<1>', k) + elif k == 'time': + v = datetime.datetime.fromtimestamp(float(v)) + elif k == 'type': + if v not in _types: + raise ValueError('{0} not one of: {1}'.format(v, ', '.join(_types))) + out[k] = v + return(out) + def _unset_parse(unsetline): + out = {} + if unsetline[1] == 'all': + return(copy.deepcopy(self._tplitem)) + for i in unsetline: + out[i] = self._tplitem[i] + return(out) + # The Business-End (TM) + for idx, line in enumerate(self._specdata): + _fname = copy.deepcopy(_curpath) + # Skip these lines + if _ignre.search(line): + continue + l = line.split() + if _parentre.search(line): + _curpath = _curpath.parent + elif not _stngsre.search(line): + # So it's an item, not a command. + _itemsettings = copy.deepcopy(self.settings) + _itemsettings.update(_kwparse(l[1:])) + if _itemsettings['type'] == 'dir': + # SOMEONE PLEASE let me know if there's a cleaner way to do this. + _curpath = pathlib.PosixPath(os.path.normpath(_curpath.joinpath(l[0]))) + _fname = _curpath + else: + _fname = pathlib.PosixPath(os.path.normpath(_curpath.joinpath(l[0]))) + self.spec['paths'][_fname] = _itemsettings + else: + # It's a command. We can safely split on whitespace since the man page specifies the + # values are not to contain whitespace. + # /set + if l[0] == '/set': + del(l[0]) + self.settings.update(_kwparse(l)) + # /unset + else: + self.settings.update(_unset_parse(l)) + continue + return() + + +def parseArgs(): + args = argparse.ArgumentParser(description = 'An mtree parser') + # TODO: support stdin piping + args.add_argument('specfile', + help = 'The path to the spec file to parse') + return(args) + + +# Allow to be run as a CLI utility as well. +def main(): + args = vars(parseArgs().parse_args()) + import os + with open(os.path.abspath(os.path.expanduser(args['specfile']))) as f: + mt = MTreeParse(f.read()) + with open('/tmp/newspec', 'w') as f: + f.write('\n'.join(mt._specdata)) + import pprint + import inspect + del(mt.orig_spec) + del(mt._specdata) + import shutil + pprint.pprint(inspect.getmembers(mt), width = shutil.get_terminal_size()[0]) + +if __name__ == '__main__': + main() diff --git a/bdisk/prompt_strings.py b/bdisk/prompt_strings.py new file mode 100644 index 0000000..3a3d10a --- /dev/null +++ b/bdisk/prompt_strings.py @@ -0,0 +1,129 @@ +# These are *key* ciphers, for encrypting exported keys. +openssl_ciphers = ['aes128', 'aes192', 'aes256', 'bf', 'blowfish', + 'camellia128', 'camellia192', 'camellia256', 'cast', 'des', + 'des3', 'idea', 'rc2', 'seed'] +# These are *hash algorithms* for cert digests. +openssl_digests = ['blake2b512', 'blake2s256', 'gost', 'md4', 'md5', 'mdc2', + 'rmd160', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'] + +class PromptStrings(object): + gpg = { + 'attribs': { + 'algo': { + 'text': 'the subkey\'s encryption type/algorithm', + # The following can ONLY be used for encryption, not signing: elg, cv + #'choices': ['rsa', 'dsa', 'elg', 'ed', 'cv', 'nistp', 'brainpool.1', 'secp.k1'], + 'choices': ['rsa', 'dsa', 'ed', 'nist', 'brainpool.1', 'sec.k1'], + #'default': 'rsa' + 'default': 'ed' + }, + 'keysize': { + 'text': 'the subkey\'s key size (in bits)', + 'choices': { + 'rsa': ['1024', '2048', '4096'], + 'dsa': ['768', '2048', '3072'], + #'elg': ['1024', '2048', '4096'], # Invalid for signing, etc. + 'ed': ['25519'], + #'cv': ['25519'], + 'nistp': ['256', '384', '521'], + 'brainpool.1': ['256', '384', '512'], + 'sec.k1': ['256'] + }, + 'default': { + 'rsa': '4096', + 'dsa': '3072', + 'ed': '25519', + 'nistp': '521', + 'brainpool.1': '512', + 'sec.k1': '256' + } + } + }, + 'params': ['name', 'email', 'comment'] + } + ssl = { + 'attribs': { + 'cert': { + 'hash_algo': { + 'text': ('What hashing algorithm do you want to use? ' + '(Default is sha512.)'), + 'prompt': 'Hashing algorithm: ', + 'options': openssl_digests, + 'default': 'aes256' + } + }, + 'key': { + 'cipher': { + 'text': ('What encryption algorithm/cipher do you want to ' + 'use? (Default is aes256.) Use "none" to specify ' + 'a key without a passphrase.'), + 'prompt': 'Cipher: ', + 'options': openssl_ciphers + ['none'], + 'default': 'aes256' + }, + 'keysize': { + 'text': ('What keysize/length (in bits) do you want the ' + 'key to be? (Default is 4096; much higher values ' + 'are possible but are untested and thus not ' + 'supported by this tool; feel free to edit the ' + 'generated configuration by hand.) (If the key ' + 'cipher is "none", this is ignored.)'), + 'prompt': 'Keysize: ', + # TODO: do all openssl_ciphers support these sizes? + 'options': ['1024', '2048', '4096'], + 'default': 'aes256' + }, + 'passphrase': { + 'text': ('What passphrase do you want to use for the key? ' + 'If you specified the cipher as "none", this is ' + 'ignored (you can just hit enter).'), + 'prompt': 'Passphrase (will not echo back): ', + 'options': None, + 'default': '' + } + } + }, + 'paths': { + 'cert': '(or read from) the certificate', + 'key': '(or read from) the key', + 'csr': ('(or read from) the certificate signing request (if ' + 'blank, we won\'t write to disk - the operation will ' + 'occur entirely in memory assuming we need to generate/' + 'sign)') + }, + 'paths_ca': { + 'index': ('(or read from) the CA (Certificate Authority) Database ' + 'index file (if left blank, one will not be used)'), + 'serial': ('(or read from) the CA (Certificate Authority) ' + 'Database serial file (if left blank, one will not be ' + 'used)'), + }, + 'subject': { + 'countryName': { + 'text': ('the 2-letter country abbreviation (must conform to ' + 'ISO3166 ALPHA-2)?\n' + 'Country code: ') + }, + 'localityName': { + 'text': ('the city/town/borough/locality name?\n' + 'Locality: ') + }, + 'stateOrProvinceName': { + 'text': ('the state/region name (full string)?\n' + 'Region: ') + }, + 'organization': { + 'text': ('your organization\'s name?\n' + 'Organization: ') + }, + 'organizationalUnitName': { + 'text': ('your department/role/team/department name?\n' + 'Organizational Unit: ') + }, + 'emailAddress': { + 'text': ('the email address to be associated with this ' + 'certificate/PKI object?\n' + 'Email: ') + } + } + } diff --git a/bdisk/utils.py b/bdisk/utils.py index 042d678..4003ef3 100644 --- a/bdisk/utils.py +++ b/bdisk/utils.py @@ -1,17 +1,25 @@ +# Yes, this is messy. They doesn't belong anywhere else, leave me alone. + import _io +import copy import crypt import GPG +import getpass import hashid import hashlib import iso3166 import os import pprint +import prompt_strings import re import string import uuid import validators import zlib +import requests import lxml.etree +import lxml.objectify +from bs4 import BeautifulSoup from collections import OrderedDict from dns import resolver from email.utils import parseaddr as emailparse @@ -25,6 +33,7 @@ passlib_schemes = ['des_crypt', 'md5_crypt', 'sha256_crypt', 'sha512_crypt'] # Build various hash digest name lists digest_schemes = list(hashlib.algorithms_available) # Provided by zlib +# TODO? digest_schemes.append('adler32') digest_schemes.append('crc32') @@ -33,12 +42,53 @@ crypt_map = {'sha512': crypt.METHOD_SHA512, 'md5': crypt.METHOD_MD5, 'des': crypt.METHOD_CRYPT} -# These are *key* ciphers, for encrypting exported keys. -openssl_ciphers = ['aes128', 'aes192', 'aes256', 'bf', 'blowfish', - 'camellia128', 'camellia192', 'camellia256', 'cast', 'des', - 'des3', 'idea', 'rc2', 'seed'] -openssl_digests = ['blake2b512', 'blake2s256', 'gost', 'md4', 'md5', 'mdc2', - 'rmd160', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'] + +class Download(object): + def __init__(self, url, progress = True, offset = None, chunksize = 1024): + self.cnt_len = None + self.head = requests.head(url, allow_redirects = True).headers + self.req_headers = {} + self.range = False + self.url = url + self.offset = offset + self.chunksize = chunksize + self.progress = progress + if 'accept-ranges' in self.head: + if self.head['accept-ranmges'].lower() != 'none': + self.range = True + if 'content-length' in self.head: + try: + self.cnt_len = int(self.head['content-length']) + except TypeError: + pass + if self.cnt_len and self.offset and self.range: + if not self.offset <= self.cnt_len: + raise ValueError(('The offset requested ({0}) is greater than ' + 'the content-length value').format(self.offset, self.cnt_len)) + self.req_headers['range'] = 'bytes={0}-'.format(self.offset) + + def fetch(self): + if not self.progress: + self.req = requests.get(self.url, allow_redirects = True, headers = self.req_headers) + self.bytes_obj = self.req.content + else: + self.req = requests.get(self.url, allow_redirects = True, stream = True, headers = self.req_headers) + self.bytes_obj = bytes() + _bytelen = 0 + # TODO: better handling for logging instead of print()s? + for chunk in self.req.iter_content(chunk_size = self.chunksize): + self.bytes_obj += chunk + if self.cnt_len: + print('\033[F') + print('{0:.2f}'.format((_bytelen / float(self.head['content-length'])) * 100), + end = '%', + flush = True) + _bytelen += self.chunksize + else: + print('.', end = '') + print() + return(self.bytes_obj) + class XPathFmt(string.Formatter): def get_field(self, field_name, args, kwargs): @@ -51,18 +101,19 @@ class detect(object): def __init__(self): pass - def any_hash(self, hash_str): + def any_hash(self, hash_str, normalize = False): h = hashid.HashID() hashes = [] for i in h.identifyHash(hash_str): if i.extended: continue x = i.name - if x.lower() in ('crc-32', 'ripemd-160', 'sha-1', 'sha-224', - 'sha-256', 'sha-384', 'sha-512'): + if x.lower() in ('crc-32', 'ripemd-160', 'sha-1', 'sha-224', 'sha-256', 'sha-384', 'sha-512'): # Gorram you, c0re. x = re.sub('-', '', x.lower()) - _hashes = [h.lower() for h in digest_schemes] + _hashes = [h.lower() for h in digest_schemes] # TODO: move this outside so we don't define it every invoke + if normalize: + x = re.sub('(-|crypt|\s+)', '', x.lower()) if x.lower() in sorted(list(set(_hashes))): hashes.append(x) return(hashes) @@ -76,9 +127,45 @@ class detect(object): return(None) return() + def password_hash_salt(self, salted_hash): + _hash_list = salted_hash.split('$') + if len(_hash_list) < 3: + return(None) + salt = _hash_list[2] + return(salt) + + def remote_files(self, url_base, ptrn = None, flags = []): + soup = BeautifulSoup(Download(url_base, progress = False).fetch().decode('utf-8'), + 'lxml') + urls = [] + if 'regex' in flags: + if not isinstance(ptrn, str): + raise ValueError('"ptrn" must be a regex pattern to match ' + 'against') + else: + ptrn = re.compile(ptrn) + for u in soup.find_all('a'): + if not u.has_attr('href'): + continue + if 'regex' in flags: + if not ptrn.search(u.attrs['href']): + continue + if u.has_attr('href'): + urls.append(u.attrs['href']) + if not urls: + return(None) + # We certainly can't intelligently parse the printed timestamp since it + # varies so much and that'd be a nightmare to get consistent... + # But we CAN sort by filename. + if 'latest' in flags: + urls = sorted(list(set(urls))) + urls = urls[-1] + else: + urls = urls[0] + return(urls) + def gpgkeyID_from_url(self, url): - with urlopen(url) as u: - data = u.read() + data = Download(url, progress = False).bytes_obj g = GPG.GPGHandler() key_ids = g.get_sigs(data) del(g) @@ -130,7 +217,7 @@ class detect(object): # Get any easy ones out of the way first. if name in digest_schemes: return(name) - # Otherwise grab the first one that matches, in order from the . + # Otherwise grab the first one that matches _digest_re = re.compile('^{0}$'.format(name.strip()), re.IGNORECASE) for h in digest_schemes: if _digest_re.search(h): @@ -146,6 +233,9 @@ class generate(object): _salt = crypt.mksalt(algo) else: _salt = salt + if not password: + # Intentionally empty password. + return('') return(crypt.crypt(password, _salt)) def hashlib_names(self): @@ -156,13 +246,18 @@ class generate(object): hashes.append(h) return(hashes) - def salt(self, algo = 'sha512'): + def salt(self, algo = None): + if not algo: + algo = 'sha512' algo = crypt_map[algo] return(crypt.mksalt(algo)) class prompts(object): def __init__(self): - pass + self.promptstr = prompt_strings.PromptStrings() + + # TODO: these strings should be indexed in a separate module and + # sourced/imported. we should generally just find a cleaner way to do this. def confirm_or_no(self, prompt = '', invert = False, usage = '{0} to confirm, otherwise {1}...\n'): @@ -194,21 +289,18 @@ class prompts(object): return(True) def gpg_keygen_attribs(self): - _attribs = {'algo': {'text': 'the subkey\'s encryption type/algorithm', - 'choices': ['rsa', 'dsa'], - 'default': 'rsa'}, - 'keysize': {'text': 'the subkey\'s key size (in bits)', - 'choices': {'rsa': ['1024', '2048', '4096'], - 'dsa': ['768', '2048', '3072']}, - 'default': {'rsa': '4096', - 'dsa': '3072'}}} - _params = {'name': None, - 'email': None, - #'email': valid().email, # Use this to force valid email. - 'comment': None} + _strs = self.promptstr.gpg gpg_vals = {'attribs': {}, 'params': {}} - for a in _attribs: + _checks = { + 'params': { + 'name': {'error': 'name cannot be empty', + 'check': valid().nonempty_str}, + 'email': {'error': 'not a valid email address', + 'check': valid().email} + } + } + for a in _strs['attribs']: _a = None while not _a: if 'algo' in gpg_vals['attribs'] and a == 'keysize': @@ -261,6 +353,45 @@ class prompts(object): continue else: gpg_vals['params'][p] = _p +======= + _choices = _strs['attribs']['keysize']['choices'][_algo] + _dflt = _strs['attribs']['keysize']['default'][_algo] + else: + _choices = _strs['attribs'][a]['choices'] + _dflt = _strs['attribs'][a]['default'] + _a = (input( + ('\nWhat should be {0}? (Default is {1}.)\nChoices:\n' + '\n\t{2}\n\n{3}: ').format( + _strs['attribs'][a]['text'], + _dflt, + '\n\t'.join(_choices), + a.title() + ) + )).strip().lower() + if _a == '': + _a = _dflt + elif _a not in _choices: + print( + ('Invalid selection; choosing default ' + '({0})').format(_dflt) + ) + _a = _dflt + gpg_vals['attribs'][a] = _a + for p in _strs['params']: + _p = input( + ('\nWhat is the {0} for the subkey?\n' + '{1}: ').format(p, p.title()) + ) + if p in _checks['params']: + while not _checks['params'][p]['check'](_p): + print( + ('Invalid entry ({0}). Please retry.').format( + _checks['params'][p]['error'] + ) + ) + _p = input('{0}: '.format(_p.title())) + gpg_vals['params'][p] = _p +>>>>>>> 69b6ec60d05d64a9e23e9a0707a0323f960a2936 return(gpg_vals) def hash_select(self, prompt = '', @@ -310,116 +441,81 @@ class prompts(object): ssl_vals = {'paths': {}, 'attribs': {}, 'subject': {}} + _checks = { + 'subject': { + 'countryName': valid().country_abbrev, + 'emailAddress': valid().email + } + } + _strs = copy.deepcopy(self.promptstr.ssl) # pki_role should be 'ca' or 'client' if pki_role not in ('ca', 'client'): raise ValueError('pki_role must be either "ca" or "client"') - _attribs = {'cert': {'hash_algo': {'text': ('What hashing algorithm ' - 'do you want to use? (Default is sha512.)'), - 'prompt': 'Hashing algorithm: ', - 'options': openssl_digests, - 'default': 'sha512'}}, - 'key': {'cipher': {'text': ('What encryption algorithm/' - 'cipher do you want to use? (Default is ' - 'aes256.)'), - 'prompt': 'Cipher: ', - 'options': openssl_ciphers, - 'default': 'aes256'}, - # This can actually theoretically be anywhere from - # 512 to... who knows how high. I couldn't find the - # upper bound. So we just set it to sensible - # defaults. If they want something higher, they can - # edit the XML when they're done. - 'keysize': {'text': ('What keysize/length (in ' - 'bits) do you want the key to be? (Default is ' - '4096; much higher values are possible but ' - 'are untested and thus not supported by this ' - 'tool; feel free to edit the generated ' - 'configuration by hand.)'), - 'prompt': 'Keysize: ', - 'options': ['1024', '2048', '4096'], - 'default': '4096'}}} - _paths = {'cert': '(or read from) the certificate', - 'key': '(or read from) the key', - 'csr': ('(or read from) the certificate signing request (if ' - 'blank, we won\'t write to disk - the operation ' - 'will occur entirely in memory assuming we need to ' - 'generate/sign)')} + # NOTE: need to validate US and email if pki_role == 'ca': - _paths['index'] = ('(or read from) the CA DB index file (if left ' - 'blank, one will not be used)') - _paths['serial'] = ('(or read from) the CA DB serial file (if ' - 'left blank, one will not be used)') - for a in _attribs: + # this is getting triggered for clients? + _strs['paths'].update(_strs['paths_ca']) + for a in _strs['attribs']: ssl_vals['attribs'][a] = {} - for x in _attribs[a]: + for x in _strs['attribs'][a]: ssl_vals['attribs'][a][x] = None - for p in _paths: + for p in _strs['paths']: if p == 'csr': _allow_empty = True else: _allow_empty = False - ssl_vals['paths'][p] = self.path(_paths[p], + ssl_vals['paths'][p] = self.path(_strs['paths'][p], empty_passthru = _allow_empty) print() if ssl_vals['paths'][p] == '': ssl_vals['paths'][p] = None - if p in _attribs: - for x in _attribs[p]: + if p in _strs['attribs']: + for x in _strs['attribs'][p]: while not ssl_vals['attribs'][p][x]: - ssl_vals['attribs'][p][x] = (input( - ('\n{0}\n\n\t{1}\n\n{2}').format( - _attribs[p][x]['text'], - '\n\t'.join(_attribs[p][x]['options']), - _attribs[p][x]['prompt']) - )).strip().lower() - if ssl_vals['attribs'][p][x] not in \ - _attribs[p][x]['options']: - print(('\nInvalid selection; setting default ' - '({0}).').format(_attribs[p][x]['default'])) - ssl_vals['attribs'][p][x] = \ - _attribs[p][x]['default'] - _subject = {'countryName': {'text': ('the 2-letter country ' - 'abbreviation (must conform to ' - 'ISO3166 ALPHA-2)?\nCountry ' - 'code: '), - 'check': 'func', - 'func': valid().country_abbrev}, - 'localityName': {'text': ('the city/town/borough/locality ' - 'name?\nLocality: '), - 'check': None}, - 'stateOrProvinceName': {'text': ('the state/region ' - 'name (full string)?' - '\nRegion: '), - 'check': None}, - 'organization': {'text': ('your organization\'s name?' - '\nOrganization: '), - 'check': None}, - 'organizationalUnitName': {'text': ('your department/role/' - 'team/department name?' - '\nOrganizational ' - 'Unit: '), - 'check': None}, - 'emailAddress': {'text': ('the email address to be ' - 'associated with this ' - 'certificate/PKI object?\n' - 'Email: '), - 'check': 'func', - 'func': valid().email}} - for s in _subject: + # cipher attrib is prompted for before this. + if p == 'key' and x == 'passphrase': + if ssl_vals['attribs']['key']['cipher'] == 'none': + ssl_vals['attribs'][p][x] = 'none' + continue + ssl_vals['attribs'][p][x] = getpass.getpass( + ('{0}\n{1}').format( + _strs['attribs'][p][x]['text'], + _strs['attribs'][p][x]['prompt']) + ) + if ssl_vals['attribs'][p][x] == '': + ssl_vals['attribs'][p][x] = 'none' + else: + ssl_vals['attribs'][p][x] = (input( + ('\n{0}\n\n\t{1}\n\n{2}').format( + _strs['attribs'][p][x]['text'], + '\n\t'.join( + _strs['attribs'][p][x]['options']), + _strs['attribs'][p][x]['prompt'])) + ).strip().lower() + if ssl_vals['attribs'][p][x] not in \ + _strs['attribs'][p][x]['options']: + print( + ('\nInvalid selection; setting default ' + '({0}).').format( + _strs['attribs'][p][x]['default'] + ) + ) + ssl_vals['attribs'][p][x] = \ + _strs['attribs'][p][x]['default'] + for s in _strs['subject']: ssl_vals['subject'][s] = None - for s in _subject: + for s in _strs['subject']: while not ssl_vals['subject'][s]: _input = (input( - ('\nWhat is {0}').format(_subject[s]['text']) + ('\nWhat is {0}').format( + _strs['subject'][s]['text']) )).strip() - _chk = _subject[s]['check'] - if _chk: - if _chk == 'func': - _chk = _subject[s]['func'](_input) - if not _chk: - print('Invalid value; retrying.') - continue print() + if s in _checks['subject']: + if not _checks['subject'][s](_input): + print('Invalid entry; try again.') + ssl_vals['subject'][s] = None + continue ssl_vals['subject'][s] = _input _url = transform().url_to_dict(cn_url, no_None = True) ssl_vals['subject']['commonName'] = _url['host'] @@ -454,12 +550,12 @@ class transform(object): def py2xml(self, value, attrib = True): if value in (False, ''): if attrib: - return("no") + return("false") else: return(None) elif isinstance(value, bool): # We handle the False case above. - return("yes") + return("true") elif isinstance(value, str): return(value) else: @@ -477,7 +573,6 @@ class transform(object): text_out = re.sub('[^\w]', '', text_out) return(text_out) - # noinspection PyDictCreation def url_to_dict(self, orig_url, no_None = False): def _getuserinfo(uinfo_str): if len(uinfo_str) == 0: @@ -622,6 +717,88 @@ class transform(object): url['full_url'] += '#{0}'.format('#'.join(_f)) return(url) + def user(self, user_elem): + _attribs = ('hashed', 'hash_algo', 'salt') + acct = {} + for a in _attribs: + acct[a] = None + if len(user_elem): + elem = user_elem[0] + for a in _attribs: + if a in elem.attrib: + acct[a] = self.xml2py(elem.attrib[a], attrib = True) + if acct['hashed']: + if not acct['hash_algo']: + _hash = detect().password_hash(elem.text) + if _hash: + acct['hash_algo'] = _hash + else: + acct['hash_algo'] = None + # We no longer raise ValueError. Per shadow(5): + ####################################################### + # If the password field contains some string that is + # not a valid result of crypt(3), for instance ! or *, + # the user will not be able to use a unix password to + # log in (but the user may log in the system by other + # means). + # This field may be empty, in which case no passwords + # are required to authenticate as the specified login + # name. However, some applications which read the + # /etc/shadow file may decide not to permit any access + # at all if the password field is empty. + # A password field which starts with an exclamation + # mark means that the password is locked. The remaining + # characters on the line represent the password field + # before the password was locked. + ####################################################### + # raise ValueError( + # 'Invalid salted password hash: {0}'.format( + # elem.text) + # ) + acct['salt_hash'] = elem.text + acct['passphrase'] = None + else: + if not acct['hash_algo']: + acct['hash_algo'] = 'sha512' + acct['passphrase'] = elem.text + _saltre = re.compile('^\s*(auto|none|)\s*$', re.IGNORECASE) + if acct['salt']: + if _saltre.search(acct['salt']): + _salt = generate.salt(acct['hash_algo']) + acct['salt'] = _salt + else: + if not acct['hashed']: + acct['salt_hash'] = generate().hash_password( + acct['passphrase'], + algo = crypt_map[acct['hash_algo']]) + acct['salt'] = detect().password_hash_salt(acct['salt_hash']) + if 'salt_hash' not in acct: + acct['salt_hash'] = generate().hash_password( + acct['passphrase'], + salt = acct['salt'], + algo = crypt_map[acct['hash_algo']]) + return(acct) + + def xml2py(self, value, attrib = True): + yes = re.compile('^\s*(true|1)\s*$', re.IGNORECASE) + no = re.compile('^\s*(false|0)\s*$', re.IGNORECASE) + none = re.compile('^\s*(none|)\s*$', re.IGNORECASE) + if no.search(value): + if attrib: + return(False) + else: + return(None) + elif yes.search(value): + # We handle the False case above. + return(True) + elif value.strip() == '' or none.search(value): + return(None) + elif valid().integer(value): + return(int(value)) + else: + return(value) + return() + class valid(object): def __init__(self): pass @@ -664,6 +841,11 @@ class valid(object): return(False) return() + def nonempty_str(self, str_in): + if str_in.strip() == '': + return(False) + return(True) + def password(self, passwd): # https://en.wikipedia.org/wiki/ASCII#Printable_characters # https://serverfault.com/a/513243/103116 @@ -693,14 +875,19 @@ class valid(object): return(True) def salt_hash(self, salthash): - _idents = ''.join([i.ident for i in crypt_map if i.ident]) + _idents = ''.join([i.ident for i in crypt_map.values() if i.ident]) # noinspection PyStringFormat - _regex = re.compile('^(\$[{0}]\$)?[./0-9A-Za-z]{{0,16}}\$?'.format( - _idents)) + _regex = re.compile('^(\$[{0}]\$)?[./0-9A-Za-z]{{0,16}}\$?'.format(_idents)) if not _regex.search(salthash): return(False) return(True) + def salt_hash_full(self, salthash, hash_type): + h = [re.sub('-', '', i.lower()).split()[0] for i in detect.any_hash(self, salthash, normalize = True)] + if hash_type.lower() not in h: + return(False) + return(True) + def plugin_name(self, name): if len(name) == 0: return(False) @@ -748,22 +935,38 @@ class valid(object): class xml_supplicant(object): def __init__(self, cfg, profile = None, max_recurse = 5): - raw = self._detect_cfg(cfg) - xmlroot = lxml.etree.fromstring(raw) + self.selector_ids = ('id', 'name', 'uuid') self.btags = {'xpath': {}, 'regex': {}, 'variable': {}} + raw = self._detect_cfg(cfg) + # This is changed in just a moment. + self.profile = profile + # This is retained so we can "refresh" the profile if needed. + self.orig_profile = profile + try: + self.orig_xml = lxml.etree.fromstring(raw) + # We need to strip the naked namespace for XPath to work. + self.xml = copy.deepcopy(self.orig_xml) + self.roottree = self.xml.getroottree() + self.tree = self.roottree.getroot() + self.strip_naked_ns() + except lxml.etree.XMLSyntaxError: + raise ValueError('The configuration provided does not seem to be ' + 'valid') + self.get_profile(profile = profile) + # This is disabled; we set it above. + #self.xml = lxml.etree.fromstring(raw) self.fmt = XPathFmt() - self.max_recurse = max_recurse + self.max_recurse = int(self.profile.xpath( + '//meta/max_recurse/text()')[0]) # I don't have permission to credit them, but to the person who helped # me with this regex - thank you. You know who you are. + # Originally this pattern was the one from: + # https://stackoverflow.com/a/12728199/733214 self.ptrn = re.compile(('(?<=(?= 1: + for item in ptrn_id: + try: + btag, expr = item.split('%', 1) + if btag not in self.btags: + continue + if item not in self.btags[btag]: + self.btags[btag][item] = None + #self.btags[btag][item] = expr # remove me? + if btag == 'xpath': + d[item] = (btag, expr) + elif btag == 'variable': + d[item] = (btag, self.btags['variable'][item]) + except ValueError: + return(d) + return(d) + + def get_profile(self, profile = None): + """Get a configuration profile. + + Get a configuration profile from the XML object and set that as a + profile object. If a profile is specified, attempt to find it. If not, + follow the default rules as specified in __init__. + """ + if profile: + # A profile identifier was provided + if isinstance(profile, str): + _profile_name = profile + profile = {} + for i in self.selector_ids: + profile[i] = None + profile['name'] = _profile_name + elif isinstance(profile, dict): + for k in self.selector_ids: + if k not in profile.keys(): + profile[k] = None + else: + raise TypeError('profile must be a string (name of profile), ' + 'a dictionary, or None') + xpath = '/bdisk/profile{0}'.format(self.xpath_selector(profile)) + self.profile = self.xml.xpath(xpath) + if len(self.profile) != 1: + raise ValueError('Could not determine a valid, unique ' + 'profile; please check your profile ' + 'specifier(s)') + else: + # We need the actual *profile*, not a list of matching + # profile(s). + self.profile = self.profile[0] + if not len(self.profile): + raise RuntimeError('Could not find the profile specified in ' + 'the given configuration') + else: + # We need to find the default. + profiles = [] + for p in self.xml.xpath('/bdisk/profile'): + profiles.append(p) + # Look for one named "default" or "DEFAULT" etc. + for idx, value in enumerate([e.attrib['name'].lower() \ + for e in profiles]): + if value == 'default': + #self.profile = copy.deepcopy(profiles[idx]) + self.profile = profiles[idx] + break + # We couldn't find a profile with a default name. Try to grab the + # first profile. + if self.profile is None: + # Grab the first profile. + if profiles: + self.profile = profiles[0] + else: + # No profiles found. + raise RuntimeError('Could not find any usable ' + 'configuration profiles') + return() + def get_path(self, element): path = element try: @@ -818,6 +1101,33 @@ class xml_supplicant(object): ).format(element.text)) return(path) + def return_full(self): + # https://stackoverflow.com/a/22553145/733214 + local_xml = lxml.etree.Element('bdisk', + nsmap = self.orig_xml.nsmap, + attrib = self.orig_xml.attrib) + local_xml.text = '\n ' + for elem in self.xml.xpath('/bdisk/profile'): + local_xml.append(copy.deepcopy(elem)) + return(lxml.etree.tostring(local_xml)) + + def return_naked_ns(self): + # It's so stupid I have to do this. + return(self.orig_xml.nsmap) + + def strip_naked_ns(self): + # I cannot *believe* that LXML doesn't have this built-in, considering + # how common naked namespaces are. + # https://stackoverflow.com/a/18160164/733214 + for elem in self.roottree.getiterator(): + if not hasattr(elem.tag, 'find'): + continue + i = elem.tag.find('}') + if i >= 0: + elem.tag = elem.tag[i + 1:] + lxml.objectify.deannotate(self.roottree, cleanup_namespaces = True) + return() + def substitute(self, element, recurse_count = 0): if recurse_count >= self.max_recurse: return(element) @@ -858,33 +1168,10 @@ class xml_supplicant(object): _dictmap = self.btags_to_dict(element.text) return(element) - def xpath_selector(self, selectors, - selector_ids = ('id', 'name', 'uuid')): + def xpath_selector(self, selectors): # selectors is a dict of {attrib:value} xpath = '' for i in selectors.items(): - if i[1] and i[0] in selector_ids: + if i[1] and i[0] in self.selector_ids: xpath += '[@{0}="{1}"]'.format(*i) return(xpath) - - def btags_to_dict(self, text_in): - d = {} - ptrn_id = self.ptrn.findall(text_in) - if len(ptrn_id) >= 1: - for item in ptrn_id: - try: - btag, expr = item.split('%', 1) - if btag not in self.btags: - continue - if item not in self.btags[btag]: - self.btags[btag][item] = None - #self.btags[btag][item] = expr # remove me? - if btag == 'xpath': - d[item] = (btag, expr) - elif btag == 'variable': - d[item] = (btag, self.btags['variable'][item]) - except ValueError: - return(d) - return(d) - - diff --git a/bin/xmllint.sh b/bin/xmllint.sh new file mode 100755 index 0000000..525d62f --- /dev/null +++ b/bin/xmllint.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +xmllint -schema /opt/dev/bdisk/bdisk/bdisk.xsd /opt/dev/bdisk/docs/examples/multi_profile.xml --noout diff --git a/docs/examples/multi_profile.xml b/docs/examples/multi_profile.xml index 1b10071..6f734cd 100644 --- a/docs/examples/multi_profile.xml +++ b/docs/examples/multi_profile.xml @@ -1,285 +1,288 @@ - - - - - BDisk - bdisk - + bdisk + - {xpath%../name/text()} - - A rescue/restore live environment. - - A. Dev Eloper - dev@domain.tld - https://domain.tld/~dev - - https://domain.tld/projname - 1.0.0 - - - 5 - - - archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz$ - archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz\.sig$ - archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz$ - archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz\.sig$ - - - - /var/tmp/BDisk - - - - - $6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1 - - {xpath%//meta/names/uxname/text()} - - - {xpath%//meta/dev/author/text()} - testpassword - - - testuser - Test User - anothertestpassword - - - - - http://archlinux.mirror.domain.tld - /iso/latest - {regex%tarball_x86_64} - sha1sums.txt - {regex%sig_x86_64} - - - http://archlinux32.mirror.domain.tld - /iso/latest - {regex%tarball_i686} - cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e - {regex%sig_i686} - - - - - {variable%bdisk_root}/base - {variable%bdisk_root}/cache - {variable%bdisk_root}/chroots - {variable%bdisk_root}/overlay - {variable%bdisk_root}/templates - /mnt/{xpath%//meta/names/uxname/text()} - {variable%bdisk_root}/distros - {variable%bdisk_root}/results - {variable%bdisk_root}/iso_overlay - {variable%bdisk_root}/http - {variable%bdisk_root}/tftp - {variable%bdisk_root}/pki - - archlinux - - - - {xpath%//meta/dev/website/text()}/ipxe - - - - - {xpath%../../../build/paths/pki/text()}/ca.crt - + {xpath%../name/text()} + + A rescue/restore live environment. + + A. Dev Eloper + dev@domain.tld + https://domain.tld/~dev + + https://domain.tld/projname + 1.0.0 + + + 5 + + + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz$ + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz\.sig$ + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz$ + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz\.sig$ + + + + /var/tmp/BDisk + + + + + $6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1 + + {xpath%//meta/names/uxname/text()} + + + {xpath%//meta/dev/author/text()} + testpassword + + + testuser + Test User + anothertestpassword + + + + + http://archlinux.mirror.domain.tld + /iso/latest + {regex%tarball_x86_64} + sha1sums.txt + {regex%sig_x86_64} + + + http://archlinux32.mirror.domain.tld + /iso/latest + {regex%tarball_i686} + cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + {regex%sig_i686} + + + + + {variable%bdisk_root}/base + {variable%bdisk_root}/cache + {variable%bdisk_root}/chroots + {variable%bdisk_root}/overlay + {variable%bdisk_root}/templates + /mnt/{xpath%//meta/names/uxname/text()} + {variable%bdisk_root}/distros + {variable%bdisk_root}/results + {variable%bdisk_root}/iso_overlay + {variable%bdisk_root}/http + {variable%bdisk_root}/tftp + {variable%bdisk_root}/pki + + archlinux + + + + {xpath%//meta/dev/website/text()}/ipxe + + + + + {xpath%../../../build/paths/pki/text()}/ca.crt + - - - - - - {xpath%../../../build/paths/pki/text()}/index.txt - {xpath%../../../build/paths/pki/text()}/serial - + + + + {xpath%../../../build/paths/pki/text()}/index.txt + {xpath%../../../build/paths/pki/text()}/serial + - {xpath%../../../build/paths/pki/text()}/ca.key - - domain.tld - XX - Some City - Some State - Some Org, Inc. - Department Name - {xpath%../../../../meta/dev/email/text()} - - - - {xpath%../../../build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.crt - - {xpath%//build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.key - - some client name - XX - Some City - Some State - Some Org, Inc. - Department Name - {xpath%../../../../meta/dev/email/text()} - - - - - - - - {xpath%../../../../meta/dev/author/text()} - {xpath%../../../../meta/dev/email/text()} - for {xpath%../../../../meta/names/pname/text()} [autogenerated] | {xpath%../../../../meta/uri/text()} | {xpath%../../../../meta/desc/text()} - - - - /srv/http/{xpath%../../meta/names/uxname/text()} - /tftproot/{xpath%../../meta/names/uxname/text()} - /srv/http/isos/{xpath%../../meta/names/uxname/text()} - /srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc - - root - mirror.domain.tld - 22 - ~/.ssh/id_ed25519 - - - - - - - AnotherCD - bdisk_alt - {xpath%../name/text()} - - Another rescue/restore live environment. - - Another Dev Eloper - - {xpath%//profile[@name="default"]/meta/dev/email/text()} - {xpath%//profile[@name="default"]/meta/dev/website/text()} - - https://domain.tld/projname - 0.0.1 - 5 - - archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz$ - archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz\.sig$ - archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz$ - archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz\.sig$ - - - /var/tmp/BDisk - - - - atotallyinsecurepassword - - testuser - Test User - atestpassword - - - - - http://archlinux.mirror.domain.tld - /iso/latest - {regex%tarball_x86_64} - sha1sums.txt - {regex%sig_x86_64} - - - http://archlinux32.mirror.domain.tld - /iso/latest - {regex%tarball_i686} - cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e - {regex%sig_i686} - - - - - {variable%bdisk_root}/base - {variable%bdisk_root}/cache - {variable%bdisk_root}/chroots - {variable%bdisk_root}/overlay - {variable%bdisk_root}/templates - /mnt/{xpath%//meta/names/uxname/text()} - {variable%bdisk_root}/distros - {variable%bdisk_root}/results - {variable%bdisk_root}/iso_overlay - {variable%bdisk_root}/http - {variable%bdisk_root}/tftp - {variable%bdisk_root}/pki - - archlinux - - - - {xpath%//meta/dev/website/text()}/ipxe - - - - {xpath%../../../build/paths/pki/text()}/ca.crt - - {xpath%../../../build/paths/pki/text()}/index.txt - {xpath%../../../build/paths/pki/text()}/serial - {xpath%../../../build/paths/pki/text()}/ca.key - - domain.tld - XX - Some City - Some State - Some Org, Inc. - Department Name - {xpath%../../../../meta/dev/email/text()} - - - - {xpath%../../../build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.crt - - {xpath%//build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.key - - some client name - XX - Some City - Some State - Some Org, Inc. - Department Name - {xpath%../../../../meta/dev/email/text()} - - - - - - {xpath%../../../../meta/dev/author/text()} - {xpath%../../../../meta/dev/email/text()} - for {xpath%../../../../meta/names/pname/text()} [autogenerated] | {xpath%../../../../meta/uri/text()} | {xpath%../../../../meta/desc/text()} - - - - /srv/http/{xpath%../../meta/names/uxname/text()} - /tftproot/{xpath%../../meta/names/uxname/text()} - /srv/http/isos/{xpath%../../meta/names/uxname/text()} - /srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc - - root - mirror.domain.tld - 22 - ~/.ssh/id_ed25519 - - - + + + + {xpath%../../../meta/dev/author/text()} + {xpath%../../../meta/dev/email/text()} + + + for {xpath%../../../meta/names/pname/text()} [autogenerated] | {xpath%../../../meta/uri/text()} | {xpath%../../../meta/desc/text()} + + + + + /srv/http/{xpath%../../meta/names/uxname/text()} + /tftproot/{xpath%../../meta/names/uxname/text()} + /srv/http/isos/{xpath%../../meta/names/uxname/text()} + /srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc + + root + mirror.domain.tld + 22 + ~/.ssh/id_ed25519 + + + + + + + ALTCD + bdisk_alt + {xpath%../name/text()} + + Another rescue/restore live environment. + + Another Dev Eloper + {xpath%//profile[@name="default"]/meta/dev/email/text()} + {xpath%//profile[@name="default"]/meta/dev/website/text()} + + https://domain.tld/projname + 0.0.1 + 5 + + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz$ + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz\.sig$ + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz$ + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz\.sig$ + + + /var/tmp/BDisk + + + + atotallyinsecurepassword + + testuser + Test User + atestpassword + + + + + http://archlinux.mirror.domain.tld + /iso/latest + {regex%tarball_x86_64} + sha1sums.txt + {regex%sig_x86_64} + + + http://archlinux32.mirror.domain.tld + /iso/latest + {regex%tarball_i686} + cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + {regex%sig_i686} + + + + + {variable%bdisk_root}/base + {variable%bdisk_root}/cache + {variable%bdisk_root}/chroots + {variable%bdisk_root}/overlay + {variable%bdisk_root}/templates + /mnt/{xpath%//meta/names/uxname/text()} + {variable%bdisk_root}/distros + {variable%bdisk_root}/results + {variable%bdisk_root}/iso_overlay + {variable%bdisk_root}/http + {variable%bdisk_root}/tftp + {variable%bdisk_root}/pki + + archlinux + + + + {xpath%//meta/dev/website/text()}/ipxe + + + + {xpath%../../../build/paths/pki/text()}/ca.crt + + {xpath%../../../build/paths/pki/text()}/index.txt + {xpath%../../../build/paths/pki/text()}/serial + {xpath%../../../build/paths/pki/text()}/ca.key + + domain.tld + XX + Some City + Some State + Some Org, Inc. + Department Name + {xpath%../../../../meta/dev/email/text()} + + + + {xpath%../../../build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.crt + + {xpath%//build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.key + + website.tld + XX + Some City + Some State + Some Org, Inc. + Department Name + {xpath%../../../../meta/dev/email/text()} + + + + + + {xpath%../../../meta/dev/author/text()} + {xpath%../../../meta/dev/email/text()} + for {xpath%../../../meta/names/pname/text()} [autogenerated] | {xpath%../../../meta/uri/text()} | {xpath%../../../meta/desc/text()} + + + + /srv/http/{xpath%../../meta/names/uxname/text()} + /tftproot/{xpath%../../meta/names/uxname/text()} + /srv/http/isos/{xpath%../../meta/names/uxname/text()} + /srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc + + root + mirror.domain.tld + 22 + ~/.ssh/id_ed25519 + + + diff --git a/docs/examples/regen_multi.py b/docs/examples/regen_multi.py index 2f92570..1ead8dc 100755 --- a/docs/examples/regen_multi.py +++ b/docs/examples/regen_multi.py @@ -1,13 +1,34 @@ #!/usr/bin/env python3.6 import copy -from lxml import etree +from lxml import etree, objectify -parser = etree.XMLParser(remove_blank_text = True) +#parser = etree.XMLParser(remove_blank_text = True) +parser = etree.XMLParser(remove_blank_text = False) + +# We need to append to a new root because you can't edit nsmap, and you can't +# xpath on an element with a naked namespace (e.g. 'xlmns="..."'). +ns = {None: 'http://bdisk.square-r00t.net/', + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance'} +xsi = {'{http://www.w3.org/2001/XMLSchema-instance}schemaLocation': + 'http://bdisk.square-r00t.net bdisk.xsd'} +new_cfg = etree.Element('bdisk', nsmap = ns, attrib = xsi) +new_cfg.text = '\n ' with open('single_profile.xml', 'rb') as f: xml = etree.fromstring(f.read(), parser) + +roottree = xml.getroottree() +for elem in roottree.getiterator(): + if not hasattr(elem.tag, 'find'): + continue + i = elem.tag.find('}') + if i >= 0: + elem.tag = elem.tag[i + 1:] +objectify.deannotate(roottree, cleanup_namespaces = True) + + single_profile = xml.xpath('/bdisk/profile[1]')[0] alt_profile = copy.deepcopy(single_profile) for c in alt_profile.xpath('//comment()'): @@ -19,7 +40,7 @@ alt_profile.attrib['name'] = 'alternate' alt_profile.attrib['id'] = '2' alt_profile.attrib['uuid'] = '2ed07c19-2071-4d66-8569-da40475ba716' -meta_tags = {'name': 'AnotherCD', +meta_tags = {'name': 'ALTCD', 'uxname': 'bdisk_alt', 'pname': '{xpath%../name/text()}', 'desc': 'Another rescue/restore live environment.', @@ -42,18 +63,22 @@ for e in accounts.iter(): if e.tag in accounts_tags: e.text = accounts_tags[e.tag] if e.tag == 'rootpass': - e.attrib['hashed'] = 'no' + e.attrib['hashed'] = 'false' elif e.tag == 'user': - e.attrib['sudo'] = 'no' + e.attrib['sudo'] = 'false' # Delete the second user accounts.remove(accounts[2]) author = alt_profile.xpath('/profile/meta/dev/author')[0] author.addnext(etree.Comment( ' You can reference other profiles within the same configuration. ')) -xml.append(alt_profile) +#xml.append(alt_profile) + +for child in xml.xpath('/bdisk/profile'): + new_cfg.append(copy.deepcopy(child)) +new_cfg.append(alt_profile) with open('multi_profile.xml', 'wb') as f: - f.write(etree.tostring(xml, + f.write(etree.tostring(new_cfg, pretty_print = True, encoding = 'UTF-8', xml_declaration = True)) diff --git a/docs/examples/single_profile.xml b/docs/examples/single_profile.xml index e267446..0692bb1 100644 --- a/docs/examples/single_profile.xml +++ b/docs/examples/single_profile.xml @@ -1,15 +1,18 @@ - + - BDisk + BDISK + bdisk + UNLESS it's in a as part of the expression. Those are taken as literal strings. --> {xpath%../name/text()} A rescue/restore live environment. @@ -24,10 +27,11 @@ 5 + items. See the manual for more information. NO btags within the patterns is allowed. --> archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz$ - archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz\.sig$ + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-x86_64\.tar\.gz\.sig$ + archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz$ archlinux-bootstrap-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-i686\.tar\.gz\.sig$ @@ -38,20 +42,20 @@ - $6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1 - - {xpath%//meta/names/uxname/text()} + $6$7KfIdtHTcXwVrZAC$LZGNeMNz7v5o/cYuA48FAxtZynpIwO5B1CPGXnOW5kCTVpXVt4SypRqfM.AoKkFt/O7MZZ8ySXJmxpELKmdlF1 + + {xpath%../../../meta/names/uxname/text()} - {xpath%//meta/dev/author/text()} - {xpath%../../../meta/dev/author/text()} + testpassword - + testuser - Test User - Test User + anothertestpassword @@ -60,25 +64,29 @@ http://archlinux.mirror.domain.tld /iso/latest - {regex%tarball_x86_64} + {regex%tarball_x86_64} sha1sums.txt - sha1sums.txt + {regex%sig_x86_64} + flags="regex latest">{regex%sig_x86_64} http://archlinux32.mirror.domain.tld /iso/latest - {regex%tarball_i686} + {regex%tarball_i686} cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + explicit="true">cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e {regex%sig_i686} + flags="regex latest">{regex%sig_i686} - + + openssh + + {variable%bdisk_root}/base {variable%bdisk_root}/cache @@ -95,11 +103,11 @@ archlinux - - + + {xpath%//meta/dev/website/text()}/ipxe - + {xpath%../../../build/paths/pki/text()}/ca.crt @@ -109,7 +117,7 @@ then provide a path. e.g.: {xpath%build/paths/ssl/text()}/ca.csr --> - + @@ -134,12 +142,12 @@ {xpath%../../../build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.crt - + {xpath%//build/paths/pki/text()}/{xpath%../../../meta/names/uxname/text()}.key - some client name + website.tld XX Some City Some State @@ -149,26 +157,27 @@ - + publish="false" + prompt_passphrase="false"> - {xpath%../../../../meta/dev/author/text()} - {xpath%../../../../meta/dev/email/text()} - for {xpath%../../../../meta/names/pname/text()} [autogenerated] | {xpath%../../../../meta/uri/text()} | {xpath%../../../../meta/desc/text()} + {xpath%../../../meta/dev/author/text()} + {xpath%../../../meta/dev/email/text()} + for {xpath%../../../meta/names/pname/text()} [autogenerated] | {xpath%../../../meta/uri/text()} | {xpath%../../../meta/desc/text()} - /srv/http/{xpath%../../meta/names/uxname/text()} - /tftproot/{xpath%../../meta/names/uxname/text()} - /srv/http/isos/{xpath%../../meta/names/uxname/text()} - + /srv/http/{xpath%../../meta/names/uxname/text()} + /tftproot/{xpath%../../meta/names/uxname/text()} + /srv/http/isos/{xpath%../../meta/names/uxname/text()} + /srv/http/{xpath%../../meta/names/uxname/text()}/pubkey.asc - + root mirror.domain.tld 22 diff --git a/docs/manual/TODO b/docs/manual/TODO index 46f2589..e1f733f 100644 --- a/docs/manual/TODO +++ b/docs/manual/TODO @@ -7,4 +7,7 @@ - in faq/ISOBIG.adoc and the doc section it references, make sure we reference that the package lists are now in the environment plugin! -- change all references to build.ini to something like "BDisk configuration file" \ No newline at end of file +- change all references to build.ini to something like "BDisk configuration file" + +- reminder: users can specify a local file source for items by using "file:///absolute/path/to/file" +-- todo: add http auth, ftp, ftps \ No newline at end of file diff --git a/examples/README b/examples/README new file mode 100644 index 0000000..8bd188c --- /dev/null +++ b/examples/README @@ -0,0 +1,8 @@ +This directory contains example files/data that you may see referenced in documentation/code. + +- mtree.spec + This file is an example mtree spec sheet that one may use for an overlay. It was generated by the command "mtree -c -K all -p /home/bts". + If you're on Arch, a port of mtree can be found in the AUR under the package name "nmtree" (it's maintained by the same author as BDisk!). + If you're on Debian or Ubuntu (or forks thereof), you can find it in the "freebsd-buildutils" package. (The executable is called "fmtree"). + If you're on Gentoo, it's in sys-apps/mtree. + If you're on RHEL/CentOS, the "extras" repository has gomtree, which (although written in Go) should be able to produce mtree spec files (but this is unknown for certain). diff --git a/examples/mtree.spec b/examples/mtree.spec new file mode 100644 index 0000000..4bfb7c1 --- /dev/null +++ b/examples/mtree.spec @@ -0,0 +1,1191 @@ +# user: bts +# machine: archtest.kvm.lan +# tree: /home/bts +# date: Sun Aug 12 01:01:41 2018 + +# . +/set type=file uname=bts gname=bts mode=0755 nlink=1 flags=none +. type=dir mode=0700 nlink=15 time=1533892836.0 + .ICEauthority \ + mode=0600 size=708 time=1533892836.0 cksum=1728088622 \ + md5=467f6490fc342ce8456face75e844629 \ + rmd160=f51a71ab0060a43b1689c23a348399e80a81067f \ + sha1=acfbd33ed54ae54f41fbb63fb48b70c25aae1a9f \ + sha256=6f0c8deb9dc85a8645d6df00d351b39a603fa66d9be50f74532c0c7130525e5a \ + sha384=eab6363fbd859d23efc7d1ccb00bb8512d9ae1c086796a2466a707c8977f0b1decd80803d2e7d7528d39caa866698d13 \ + sha512=b998e3c87df1dd4ec29786f7a4a2ebc5812460d8094523db09dc9892f4f26eb662d1d2dceb900343999577e3680d0a299ece47689ead37e4d4a8f52697f62c7a + .bash_history \ + mode=0600 size=37 time=1533891009.0 cksum=4199550415 \ + md5=78ae1822a93a915c47ada82b6c056891 \ + rmd160=1bc6e4c6e5016930816f42de9868da9326b67c7a \ + sha1=4d435652d22a134f22a0f9caba494be4c3cf4c74 \ + sha256=75aab51f58822107222a86866409d0b0c0ffb5bca5f9c76b8ebdc3262be10132 \ + sha384=d882fad4cc55c40ff8d436a590763fee0a2353d939f488fcfd48ecbf1870c45a46af32e1508fdeed48aaeafe5aec9e4d \ + sha512=a11ba3a1c49f972596adf01b8c3ffa6db7b9924faf1d9c6b116d7f3a35a498a07af908497ea574ef14d6c2cb23d312311d071829d9e0ba7bf63b1a884a2fd562 + .bash_logout \ + mode=0644 size=21 time=1528102451.0 cksum=1184899666 \ + md5=42f4400ed2314bd7519c020d0187edc5 \ + rmd160=cc717ce43231530db7d82918f7cb933a4310497d \ + sha1=9fd0cfda5b85651169f8761a834941b1f6f53952 \ + sha256=4330edf340394d0dae50afb04ac2a621f106fe67fb634ec81c4bfb98be2a1eb5 \ + sha384=8b8cdafcbcd185b945d1932f6466551c6ac2211c5cf2c837440049597e20ba62875d5a24bba6c015d322ff8ba28f88cf \ + sha512=9da5cbf2044ff23f2bfb80d4dca074f0e4a753d7260ca4730de6ffde08471ca08c2fa211d3ae9e65c508f5eef8c90c862b1f0d1b16e3610aa85a6eea1bb29add + .bash_profile \ + mode=0644 size=118 time=1533887343.0 cksum=2231410758 \ + md5=eaebbf73ecafa127a567c344811a6376 \ + rmd160=a2ab0e32463506202c6ec87368f45acf3fd02302 \ + sha1=9f6b9b4c3cac8cbbd528b21a15b73fd954e4f6f6 \ + sha256=920c4e0d2496be498315a00230280f177c3ba57f62b65dfd7e1768550b97ee75 \ + sha384=b95c22c85a7d86872f903239b9eae36921bb98d84fef7e12a754f3fe8b3982763b3daaa856ec4560da7d807873b06505 \ + sha512=c40b60cb8f7c1d070a2591d54c2c4cfdb3a9906d3a224f3aeb68172b056632c72137c3db7d67fcef65ddd487c2224dd5632c1b5aa6acfc851df2d2a3484c4542 + .bashrc mode=0644 size=141 time=1533887343.0 cksum=1724252355 \ + md5=027d6bd8f5f6a06b75bb7698cb478089 \ + rmd160=14f93310fc9178989b6bcd82016fb9c343c12711 \ + sha1=a573cf76b3cdc72928add2585f68ca97b54b6d33 \ + sha256=3e22bf86ae6708df7a6bceb88c67a00118275f9c0b5268f453dd388af7c43b53 \ + sha384=7213aeab4945c3116bb8a5362de15dd720837e2b23f5c333b3672d30f7296d0383be6d9de30a6ee2b252db25d25533ab \ + sha512=e5171e7260978195878ccbc8e4ba42ecc3a10d976722b00295b895387dc797629a0f6368036856eed7cce93245a7ef5f427dd0cf4e0750a25fc621218ef0ec8f + .esd_auth mode=0600 size=16 time=1533892197.0 cksum=2537222975 \ + md5=d0ccba4b80aec29b45c30e11c99dab99 \ + rmd160=1f996245e34773c5dc27d4a6a2c8abdc6eee2bb3 \ + sha1=ebf538413698088cf006a9fb806d93c377681d93 \ + sha256=44ac8fdabeac546d34f0c07c9669b15989945524ea0c970ff3a68e5fb29f9718 \ + sha384=4aab605d0fdb33c4a2d471ac2f74a396bc9dbca302cc4927ac254fd68b33fee1e92f4aa9c690e5206268cdce10fd6bf6 \ + sha512=162b117e144ef6fc2652e7c53e67b367bd5a78652d5b5c6443db095be6080d17ccc073ca523574d32bbdc09bc9112863c2a34300822671f4137603b3e4a18002 + .hushlogin mode=0644 size=0 time=1533887343.0 cksum=4294967295 \ + md5=d41d8cd98f00b204e9800998ecf8427e \ + rmd160=9c1185a5c5e9fc54612808977ee8f548b2258d31 \ + sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709 \ + sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ + sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b \ + sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + .profile mode=0644 size=61 time=1533887343.0 cksum=2121637737 \ + md5=f4fa4426f8fe851d550ec4133ae7a591 \ + rmd160=b6c83aa6e63ad5072e77fb66baacacd0ce86efbb \ + sha1=3cad7f9db034efb59c7b954caee1254093b7e314 \ + sha256=82ac2f2daed0a768264ba7e8a557fed3f50174763d320151bddd9ea177fb7060 \ + sha384=e5e6c1c9803feae3fc7feed97177d46b4b20e648afdbf2d2ea59236a712793a020c15c35614bb00f628350fb3107af24 \ + sha512=8645fe3f6314da589e55a8a2ff395125aa9e8d274b1e71d67697a9dec9abdd5267d5f4ed736108671ad778f3a3f89466aeb84a6e5d6598a87ceddb9d4ba3b3cc + .viminfo mode=0600 size=1351 time=1533887339.0 cksum=204212364 \ + md5=a9aa5eb942f7f94c0205d4bea5641765 \ + rmd160=5e792c16c7699711dfbf5273a49febb014daa62d \ + sha1=471f4950c354b89adca2e0e0c22e548043a0f620 \ + sha256=c8e27b7252972ece9cb8072fd7acecf4d26f342566ebb9570a37bd0a49bb00f3 \ + sha384=74658a156196de81d9abdc40c2a9fdadb0be4fbbc3d4b8702accb1113ac1dd3a4d8748b4a81f22a92e10bf5a09384bef \ + sha512=4916562ae446d86415138fd48d6058c0272546aeb92659b9a15fcdb263f17441f33899623c82b58ad79dc8ac65cddb02bb22891f53e19319dc51ee732883a4d7 + +# ./.byobu +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +.byobu type=dir mode=0755 nlink=3 time=1533887343.0 + .screenrc size=0 time=1533887343.0 cksum=4294967295 \ + md5=d41d8cd98f00b204e9800998ecf8427e \ + rmd160=9c1185a5c5e9fc54612808977ee8f548b2258d31 \ + sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709 \ + sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ + sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b \ + sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + .tmux.conf size=0 time=1533887343.0 cksum=4294967295 \ + md5=d41d8cd98f00b204e9800998ecf8427e \ + rmd160=9c1185a5c5e9fc54612808977ee8f548b2258d31 \ + sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709 \ + sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ + sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b \ + sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + .welcome-displayed \ + size=0 time=1533887343.0 cksum=4294967295 \ + md5=d41d8cd98f00b204e9800998ecf8427e \ + rmd160=9c1185a5c5e9fc54612808977ee8f548b2258d31 \ + sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709 \ + sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ + sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b \ + sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + backend size=19 time=1533887343.0 cksum=3535255992 \ + md5=b9de32e8600d5615506e5212895999bc \ + rmd160=5573a8bec6c9526c2d6f32f41fa1d3ff47f13f11 \ + sha1=fa27cfbaa0e63e555cf62ea2f8401215049b6f41 \ + sha256=0d93afa2a024ee947cd2e93f16757a995242ca67c6bc2e4bfac413a97c6dc665 \ + sha384=c3b608895a47c244796077437eeef5e7c573a69e3070f3cf9cf95fbb6175feaf4e20c400e7513b24175ee64d33af98c3 \ + sha512=afc1d2bf323a2288fda11f6344b61ef7e4dc767e228ebdc520857b6e4776dd3dd4f136986e46fa5c99e305f6cb3d5da929cd817f1ddbd9dd8b709f3ea498c471 + color size=38 time=1533887343.0 cksum=2038486494 \ + md5=221bcadbadbc1b91c5539bd022922aee \ + rmd160=8b3a767a8974ad34c6c49ba8f4f0818ad31bb9a3 \ + sha1=7b96f80ebb6f621585a5a80c784d02c287e27667 \ + sha256=ea5675cbc76e84896d0d9e66f9c1e7a7a7e817dccbc723d5e72e9d53919378aa \ + sha384=20e3a9c2a8e7a1601bbe7fe7d5ff7d0be192e90c9ec8b51a6ea79b1a35c8b100de51e99ca6886de93bb932e4dea4ca2a \ + sha512=3b0d122fe4dc775514ccdd7e48e97f5ef437474c1a73374d0b638cdfd7c70909c31e9739fe64756cf1b819b74a9ab78deb76d617e813af0f907bdba396ce2a34 + color.tmux size=96 time=1533887343.0 cksum=2170335825 \ + md5=26aba37629a70cb820f0ec29c8bf6fc4 \ + rmd160=b9554a1e3762b772282cb739c29a139c757a6c8a \ + sha1=2ce6c8660fa978b7dbf6fd8702fef564a83b9b2e \ + sha256=d8d930ddaeedffaf161cea5d12351998417d0c433dd93b5c3ecd17547077af98 \ + sha384=dbca81d7eaece86e6baa59844cffaa31538b9da0e8c525c8253f890d2036eeb0ac4b3663ea3d47e86607f129929b12d7 \ + sha512=2037b8fcb93b771b6d404163d81925f5d240ee6498bd5690dd956ca992d424cdbbacc07b3529adeffd71bfc9b424daf16d18494f4d3c819a3806ffafe233b465 + datetime.tmux \ + size=45 time=1533887343.0 cksum=2706742568 \ + md5=8240377785c7f31d7ebafc146ff1aabc \ + rmd160=06cf4f17b60253123e36c4aea27332c49764d1f5 \ + sha1=93f931e68346659d11a6c90fc4ddbf0afa69db65 \ + sha256=1f968b922c32891614d6bc42f46cabb3340ae121ba8be7bfe22969cbf5485741 \ + sha384=b5e578907180807e0ee70ee8ad4f7c1a1ef7a02b4895b164003fa06ec8b6d26b8b38a88c20f2702859868bfee928c811 \ + sha512=7f57b2814be798e42cd805809e585a72dc00f7553b3eff1b6216755183be81c4447bc43043f8fbcb5f938ad981672a7e4fc30affe15c5d4f0c748427dc096ada + keybindings size=52 time=1533887343.0 cksum=554879383 \ + md5=3b208b56b29dcc53cc5ea24e5427f284 \ + rmd160=43a96cbb89c989e2194857132aa4e13610105bc9 \ + sha1=b64db055af3de45e7e73ef8eb473a8925e16a328 \ + sha256=3628c03bafa7e675e03606e444d83893ea6c1126ed4a130993e401d4ded3fa00 \ + sha384=2b659063c32affb52437988928b0bbc6a55b3b2fad0da9116a5023f0abc427d8739526c2f625193f3403851e5b5fd89d \ + sha512=9a123747298ea6d9151fad2670f5a971191bab795926329a8e85809765eddddafdfd99dc32bfe25e480575ecd7dbc3ba5e182c596f20ca3640730a9fb0951f53 + keybindings.tmux \ + size=72 time=1533887343.0 cksum=596856748 \ + md5=2319ccd3a8af838cc04bb15b186fa533 \ + rmd160=30e6da136b166be6b2b32bf55a1fe03658455522 \ + sha1=fe1c5f855ffab04cc42ddfda19aca32f98bad0fd \ + sha256=751768997b89faf43a3856b1946b6cff2219c8a0c9b579de5eae684b0f54d3ba \ + sha384=076c31298163e1826c3b329e5c535b526fa2e93fc47c5d5c2c9c33b0d245ed7a5429d3f34a4769e0da3647eb410e205f \ + sha512=194ff50620f5b0ba9e0809b6749401a833cad3bf05a0a6197b7e89a5ffd52c83dd7093a89bd84c77a0865f5e0ec9373aedf4c0ad1145aaf4ca3a14fbe3256f2b + profile size=49 time=1533887343.0 cksum=3874606283 \ + md5=4b7b504e27ce89863266136c6cf69c1f \ + rmd160=2f1bdea88098c284c3439f842ec4c5a8949ac3c7 \ + sha1=04ffef8063881c1383b187231d5d7b7a26b10d93 \ + sha256=5e4f51354894e81450e90668a5877c003bbd85b624fcbb390dcbcc9e0c49a599 \ + sha384=f5b734658a7dcbb41efb1d842bf1a674e9f07b6b676753a092b6bc662973760b3c1406cec7fd5bc49973a0dcc81d2018 \ + sha512=0c751a4a0e950fb6df6dcf9eafb28c86253788dbafe3a4b477926b4f6236c38cde05096784dd096bfee8b2153a8a284b61c3732a3267f2096e30dfa6239417d7 + profile.tmux \ + size=47 time=1533887343.0 cksum=4193456139 \ + md5=9e78cfb64247770ca75e4b107db42eca \ + rmd160=cc05e4b367b7bb45f4700ad75e930918f97bedfb \ + sha1=2614df9ef67fa4197b831789d79b7f1192f9264c \ + sha256=97439c5c95f8019a6dbb098c49dc7098f810654513770a950e9c841aceb80d1c \ + sha384=d53763d9cd6a72561d96122fc44684e72e4df4d70b6d32e902bb58c05a63803d8d2b18640dd40d7b9732794eff6a38bd \ + sha512=9930f4293f4337dc4e2d44e4b576300ab15ed053ad95aa6cc5656c1703b7914cc0866c50a39b0d9b387a394b2fd3f7f22e1ba1445020862d156f9fcb111dba97 + prompt size=94 time=1533887343.0 cksum=2599587319 \ + md5=0ad9ddd249b099cf9930823b9678e620 \ + rmd160=8dab7b8384aa8dace784c73e66a1ccdf490eadd9 \ + sha1=db22e7c7ba4efc94455acf93e19cb792c085e9f7 \ + sha256=f37e78647464978dd49199e539c7ff22be38a84fe0d2580e414b03908a2ca01a \ + sha384=61353eb6feeab321d2140b50c853d6ee39638a48486dced97bbf242857c244e1f2c7287685d8ea71a338a3092df53672 \ + sha512=17a4f6ab72fcf56eba0f34e6b93647a6fc38c8d30a471761ef336b8e56c89190068786863039a9228cedc470d7567e9cc1dfb67d5a5dad1986f002b61beff3c8 + status size=2828 time=1533887343.0 cksum=2232803247 \ + md5=44a329eb7e8989d68bb455fdf777451e \ + rmd160=82d8c48477f4fba778a5252af1255158a7560633 \ + sha1=e2d6181beb300b39724aa8c5c59f08c000c10952 \ + sha256=5dc0dd92b272e1112e205890d46a374fb73d5408546ab292edf273d5d9da5629 \ + sha384=638fd8c3ff6eb85991c8d096a96f3b90b771b0e94e41238ed3f4afcf9f6590d053a20f4abd67124b516abda727e084e8 \ + sha512=2f7af7e8b65c52c21c6eefa770d60a2446b673d75f2020fd77cbb4d307fc57a822baf484b32006a2c84428518f106cb3c8c93aced40987e59edb6a0600a3149d + statusrc size=2536 time=1533887343.0 cksum=3609469076 \ + md5=2a3b01cdd4d5ae2b34d0a0d44c29666a \ + rmd160=912f95898d4c3fd196d59d3a5e27321fafeadafb \ + sha1=fda3cd61fc951e10225996114705121308f75a38 \ + sha256=2588070e7c4405a3f34463561e8516df452d5b6389e05f491edb1eb1d521ff10 \ + sha384=62af199d31917340b55c5d7f767e348d09ded710ac734c05b66a0bb36deb49091671740a965a15eb32eed915006a42ee \ + sha512=b0bf1364a6150e5b2a530de92a6baa86911f233b13d73c5d511164d7aa7f143ff31032824ac3795f5578c8e20382103c183eb2d9e7effbd047e6b14007d4dbd0 + windows size=0 time=1533887343.0 cksum=4294967295 \ + md5=d41d8cd98f00b204e9800998ecf8427e \ + rmd160=9c1185a5c5e9fc54612808977ee8f548b2258d31 \ + sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709 \ + sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ + sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b \ + sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + windows.tmux \ + size=0 time=1533887343.0 cksum=4294967295 \ + md5=d41d8cd98f00b204e9800998ecf8427e \ + rmd160=9c1185a5c5e9fc54612808977ee8f548b2258d31 \ + sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709 \ + sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ + sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b \ + sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + +# ./.byobu/bin +bin type=dir mode=0755 nlink=2 time=1533887284.0 +# ./.byobu/bin +.. + +# ./.byobu +.. + + +# ./.cache +/set type=file uname=bts gname=bts mode=0755 nlink=1 flags=none +.cache type=dir mode=0700 nlink=10 time=1533892203.0 + +# ./.cache/ctrlp +ctrlp type=dir nlink=3 time=1533887303.0 + +# ./.cache/ctrlp/mru +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +mru type=dir mode=0755 nlink=2 time=1533887303.0 + cache.txt size=21 time=1533887339.0 cksum=2617362503 \ + md5=1104fb548a18bddd784d01bbeccd0a9f \ + rmd160=9b84d88d0941157f3843418d74ccc849ab8c271f \ + sha1=fd76380895df14d112d6422485b2cfaabf5ee617 \ + sha256=7b3db0ef6cb9d5eeb8907363c53c60d7b28359b754ff9c2e1c6773b62f4c12fa \ + sha384=ca474c76a8b6142d281fb2fb458419fa95d0d929ecd29fae070597ff78da7c5d4cf38784eb59dc5fe32b72c0c0f71f83 \ + sha512=64d7067048e84ce7e2ce801909527dcb60c71be55e0acbb7e34e315ebb9d99812d6626f1e4a7b76c0f7249c703f1a4d4364ed255e9ef321dc406bd578aeffba8 +# ./.cache/ctrlp/mru +.. + +# ./.cache/ctrlp +.. + + +# ./.cache/evolution +/set type=file uname=bts gname=bts mode=0755 nlink=1 flags=none +evolution type=dir mode=0700 nlink=8 time=1533892198.0 + +# ./.cache/evolution/addressbook +addressbook type=dir nlink=3 time=1533892198.0 + +# ./.cache/evolution/addressbook/trash +trash type=dir nlink=2 time=1533892198.0 +# ./.cache/evolution/addressbook/trash +.. + +# ./.cache/evolution/addressbook +.. + + +# ./.cache/evolution/calendar +calendar type=dir nlink=3 time=1533892198.0 + +# ./.cache/evolution/calendar/trash +trash type=dir nlink=2 time=1533892198.0 +# ./.cache/evolution/calendar/trash +.. + +# ./.cache/evolution/calendar +.. + + +# ./.cache/evolution/mail +mail type=dir nlink=3 time=1533892198.0 + +# ./.cache/evolution/mail/trash +trash type=dir nlink=2 time=1533892198.0 +# ./.cache/evolution/mail/trash +.. + +# ./.cache/evolution/mail +.. + + +# ./.cache/evolution/memos +memos type=dir nlink=3 time=1533892198.0 + +# ./.cache/evolution/memos/trash +trash type=dir nlink=2 time=1533892198.0 +# ./.cache/evolution/memos/trash +.. + +# ./.cache/evolution/memos +.. + + +# ./.cache/evolution/sources +sources type=dir nlink=3 time=1533892198.0 + +# ./.cache/evolution/sources/trash +trash type=dir nlink=2 time=1533892198.0 +# ./.cache/evolution/sources/trash +.. + +# ./.cache/evolution/sources +.. + + +# ./.cache/evolution/tasks +tasks type=dir nlink=3 time=1533892198.0 + +# ./.cache/evolution/tasks/trash +trash type=dir nlink=2 time=1533892198.0 +# ./.cache/evolution/tasks/trash +.. + +# ./.cache/evolution/tasks +.. + +# ./.cache/evolution +.. + + +# ./.cache/flatpak +flatpak type=dir nlink=3 time=1533892202.0 + +# ./.cache/flatpak/system-cache +system-cache type=dir nlink=3 time=1533892207.0 + +# ./.cache/flatpak/system-cache/summaries +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +summaries type=dir mode=0755 nlink=2 time=1533982908.0 + flathub size=3989440 time=1533982907.0 cksum=3357406845 \ + md5=1962cdc7e3489217e85da1ab76fea2e2 \ + rmd160=9e33530ef7195ec32fccb6ea0068baeba6b05745 \ + sha1=56ba3347d2fc7591065d8ebf944a0f2b4e5d4a53 \ + sha256=75867f0fd5ab8e53505f4e97b3accc98216cf3d13db99959f468f5fe7e7b8d73 \ + sha384=369f87758778cc79ae0ac830bb3809c453510bea0b461057001a0ef07e797c24df9cbd5f7e5db52a45519897d574576d \ + sha512=ccb8ef9c3ecef187afb1dcc3bbffbadccd4641bffbb6b02fdc934697e5236d007632eadbf790465f62a12e8f536deae6027bfb7dc13a279f0bb81e88d9f976c2 + flathub.sig size=569 time=1533982907.0 cksum=573001640 \ + md5=a2d22644c856db9cd0359cb7f421a331 \ + rmd160=71dda896293f3529b65cac9d6784dd4ecff77a49 \ + sha1=b83399a302df12959c46db9715083248889beef2 \ + sha256=c6ae865caea3496d5f88195621f73fd949788f7c818dd6b0fd3066a2d9c2f6ff \ + sha384=96ff693a3595469ccc502092ada5074da6f522ab4e47038fcb2f22c0decaf3a8ce506b65f84d7f0035e637edf484410d \ + sha512=4fd61424eade6d47479ca2625d28286d078cf274443eea0913c7d0bc0e81b74a65cc395083f4aff24deb9801e69c760db86a12fd4cfc9de5157ea934e8407c77 +# ./.cache/flatpak/system-cache/summaries +.. + +# ./.cache/flatpak/system-cache +.. + +# ./.cache/flatpak +.. + + +# ./.cache/gnome-software +/set type=file uname=bts gname=bts mode=0755 nlink=1 flags=none +gnome-software type=dir nlink=5 time=1533892261.0 + +# ./.cache/gnome-software/fwupd +fwupd type=dir nlink=3 time=1533892205.0 + +# ./.cache/gnome-software/fwupd/remotes.d +remotes.d type=dir nlink=3 time=1533892205.0 + +# ./.cache/gnome-software/fwupd/remotes.d/lvfs +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +lvfs type=dir mode=0755 nlink=2 time=1533892205.0 + metadata.xml.gz \ + size=44744 time=1533892205.0 cksum=1675012442 \ + md5=d2e80bec8d6f34c2b6f9e2bf86ebc01c \ + rmd160=0b24bfe05d7bc50119fba2f68d9b8bb71d777e5c \ + sha1=c9df690436a3502b39f1ae0f52b64a0cb9d55290 \ + sha256=c2c20af7e7e0b3766ca8710a33afba8fedf5c71ac3ae016e080fd0765d3653a8 \ + sha384=0b044a3e27753e84cb9f326c84f32dd4b7c86afc4c2eed46a2b00fc2711a6cee82eb68530d606312b2a03327847baee2 \ + sha512=fe95b7076584d52b457ccc8e36c4ac56e7cdbbe86970f61837aa70bcab746be76f7de90a2a9785ea803634cbd578ecb73878658942855e75e13dd4da87b21d38 + metadata.xml.gz.asc \ + size=490 time=1533892205.0 cksum=533219609 \ + md5=6ba8f9aede101216dcac5ea4ed9fc5e2 \ + rmd160=7e1f293347adef3e53139b352fd93dacd4fc5020 \ + sha1=d6a610b1dbfbc78e5d7ba7b563b52ddece15bf16 \ + sha256=905ef78fa581e93575bc6aaa12df8bda3b24da32a211bb63d7933a04707e5e65 \ + sha384=b4dacb0ae588635ae7f2d17905b6b9994845b5974ded28011cf3d46882d5bb0dab6eda7aa4995c399479432ba58678f4 \ + sha512=ab7c43818b6e06d7438bf9864c9586d8937a152343b17f8dadf726d511ef31c8dbe1cd4cb468447b343bdf6a84a04a7115b3ed28bd6c1690d9ef8587d6547e49 +# ./.cache/gnome-software/fwupd/remotes.d/lvfs +.. + +# ./.cache/gnome-software/fwupd/remotes.d +.. + +# ./.cache/gnome-software/fwupd +.. + + +# ./.cache/gnome-software/odrs +odrs type=dir mode=0755 nlink=2 time=1533982926.0 + ratings.json \ + size=135331 time=1533982926.0 cksum=3487833939 \ + md5=9ab456e5b36238ecea4b521623b65d8d \ + rmd160=30e66a2b92e8d864db1b5da785f36c5359fafe6e \ + sha1=5b7c619cc684ff8b58b9515e688f032c879d4fc0 \ + sha256=095a514fd8379b70463ff27b1b82d85b9d3112dcc4bc647c9dfc68ad6f162898 \ + sha384=9ba52a91195bfafd25c7a92a9a61df625fab0ad6d56077ed82322c5bdc24363462feaee97f73ff53ccced57623d4a976 \ + sha512=d7d4880779ebbf8891974ebc94f9fdb33adfbbe2eec1864f9603b27616630203e38e7b00fbf1ab01ecfa6f40033fbc01e47f27160e47765cdafc3b72bc520325 +# ./.cache/gnome-software/odrs +.. + + +# ./.cache/gnome-software/shell-extensions +shell-extensions \ + type=dir mode=0755 nlink=2 time=1533982904.0 + gnome.json size=689340 time=1533982904.0 cksum=1985329461 \ + md5=48b2556d5c40de157b8ec226f1823681 \ + rmd160=cb184d7bbf07f46150c982d7b1f9e7a96290a0bf \ + sha1=918ad9dbcacc158fb54ae38edf66e87f1734f7ca \ + sha256=a586084bde502e1b918df63272b6119c3354e9928250fd4fd72cdbbe46c2e06b \ + sha384=74620eba249416a09415d02a15b5c83c566d28e56dc1c6c0af634bd6a305b47605989cab9b02aa74be94a6f467e17407 \ + sha512=0815326e9ddc91c99f6f6897d69e1faaa34807c52b6062b89d9c9068ea42142846ddab08003f253814ab4c45503d7c21426bb3ebe7b4a8c1ed00895ffd9549e4 +# ./.cache/gnome-software/shell-extensions +.. + +# ./.cache/gnome-software +.. + + +# ./.cache/gstreamer-1.0 +/set type=file uname=bts gname=bts mode=0600 nlink=1 flags=none +gstreamer-1.0 type=dir mode=0755 nlink=2 time=1533892204.0 + registry.x86_64.bin \ + size=659022 time=1533892203.0 cksum=3598088323 \ + md5=29c7854851b972bd75ccd693966458a2 \ + rmd160=bcff2fa1593069f7add90be43683dbf32753785e \ + sha1=8b0369bf17db47b2bb8954d6435639bf0b0b4a88 \ + sha256=4125ba384bc64ca020b9cddd941abd4a23d31239bfb845ed95eb93df13e4c0da \ + sha384=81b0c2868dfa5e16b62f672ee2cb44d698c4ba667271651d8e28e3bae4d0c2844b312012b9200e6dc1ae450255666fb9 \ + sha512=c6fcbc0066e97a1eb570e14931ec475c97c90531c91fa3ea9d7aea1c09b8430c1f73cc6e9b3244c46f0990cffb2f5ce8321dcb0856de347b1ca578e25fe6c7b5 +# ./.cache/gstreamer-1.0 +.. + + +# ./.cache/libgweather +libgweather type=dir mode=0700 nlink=2 time=1533892198.0 +# ./.cache/libgweather +.. + + +# ./.cache/tracker +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +tracker type=dir mode=0755 nlink=2 time=1533892842.0 + db-locale.txt \ + size=11 time=1533892200.0 cksum=1299461343 \ + md5=95a7bb7a8d0ee402edde95bb78ef95c7 \ + rmd160=db3e01a37dc26867c6a0f90c72c8a511cb049926 \ + sha1=2f7b4e2832e68e1b002423d9459c63734eef3941 \ + sha256=65ce8c93514741c928d27fcdbe7b0a754dfcaebcb2292111cd60305dc983f51f \ + sha384=fa222c070eff0125eedaf2977fcc24e8d29da4091b6fe2bfa7b0aea06f49aeca795c99d8fb6119387659c2375a414507 \ + sha512=f4bf1e5a254e0454f9fce50b42de03559f14be007ba7abf0bebefb61ab296265b28d9ce645b961b1a1e1f9d0245b786e76227961dfac81ac85512800f66971c9 + db-version.txt \ + size=2 time=1533892200.0 cksum=2793752979 \ + md5=1ff1de774005f8da13f42943881c655f \ + rmd160=c5c50c39f9b5de2dbb00e9ee07be15b276cec45c \ + sha1=4d134bc072212ace2df385dae143139da74ec0ef \ + sha256=c2356069e9d1e79ca924378153cfbbfb4d4416b1f99d41a2940bfdb66c5319db \ + sha384=ac379cd723f549e2410050ae58541ca19f0cafbff2e83b2cbf07b5a4e20d1aa3f8060bf3c6d62980380323ba46a0a8fe \ + sha512=c0033b5f5a4815a172984d64037dd49a8663fb8b3a71e47f11ecd332c8c3819c57e1631fdf46d66c6ff0e58763a61529fefcfa2a6675e186ee901e5452fedd94 + first-index.txt \ + size=5 time=1533892217.0 cksum=812303520 \ + md5=5337810b0c500ace9c4cd745f4b8bcf5 \ + rmd160=468b466ac6f52d13fe74564648e20933784a70ce \ + sha1=d1325bb186bd83303245e504f7c6eceae7f19e44 \ + sha256=8248f7772687aff0b103b36898908deb82126337f933e49a5ac04829093c58ff \ + sha384=569c13b976df33f10c4bf03bb0a81705344be315cbc8139f86ccdacee50425bdd523452096a526fb7b5c49ffe0297793 \ + sha512=5753b285d3f7881f06c35bb16931cf2a5408f13701855d5c7b2810f34e9bb266392d80ff15253a79f89d30a9ed0eec3be0e7e81677a7dbfa1e15a3907a50b02e + last-crawl.txt \ + size=10 time=1533892217.0 cksum=3333845165 \ + md5=675af9ce63ea7511baee3181bda1c5d2 \ + rmd160=e93b2399eba53f8042bd8500ad746413fa79dd27 \ + sha1=26f4a7a8fdc27c4673c769ee3d51b4ba2172c13c \ + sha256=82b3b3c9c0d6a4db991b70917f3a03aa26d920c9cc099abf5127cde1ff0126cf \ + sha384=82417a75a071d4c2952b768380ac8802a4c467a0d2aed4630341a1db42ee22afe70b2cc59a17b53cecaa84e17a09778b \ + sha512=7ba2263c8288cc9810148f142ea17b1c0bb93ba3f2572b91e26715dfc669a0851f287ebafc41a23fb90c5c754283dcb8a610960342c12b9151d916365f48cc37 + locale-for-miner-apps.txt \ + size=11 time=1533892842.0 cksum=1299461343 \ + md5=95a7bb7a8d0ee402edde95bb78ef95c7 \ + rmd160=db3e01a37dc26867c6a0f90c72c8a511cb049926 \ + sha1=2f7b4e2832e68e1b002423d9459c63734eef3941 \ + sha256=65ce8c93514741c928d27fcdbe7b0a754dfcaebcb2292111cd60305dc983f51f \ + sha384=fa222c070eff0125eedaf2977fcc24e8d29da4091b6fe2bfa7b0aea06f49aeca795c99d8fb6119387659c2375a414507 \ + sha512=f4bf1e5a254e0454f9fce50b42de03559f14be007ba7abf0bebefb61ab296265b28d9ce645b961b1a1e1f9d0245b786e76227961dfac81ac85512800f66971c9 + meta.db size=3686400 time=1533892857.0 cksum=3661685851 \ + md5=5a85dfedeb76d31b907b4140bd5080ef \ + rmd160=545e85f228eb1398458356e55cb4ed7953e2a483 \ + sha1=31c7127cb32f4a34cb040cdfb789ca1de1c97aec \ + sha256=916ceb30e76082d79bce655aae24984ffe5441f914afb349e7a0ab49d83cd8ea \ + sha384=65b5c8ffb506598fc141b553a8c676c2f6309e2a77df1d95a4c7188f2a01a1528f2be7c069d5fbd6e001ee371c6a0f09 \ + sha512=181cdbecdeac5edd57b040164ae06bc00466618c3881afa007a10cbcee795798a0d21b24f50f08ff730b3c77dd0998b2f20ba4d622acf7526416ea7be7821ec7 + meta.db-shm size=32768 time=1533892857.0 cksum=633931712 \ + md5=237e8655541107278aec6725b19acfea \ + rmd160=ddf77adfe5f4738ebe1518f7a2ec4cf933836de1 \ + sha1=f675bc0815bef1648fc822e137771dbc983feaa7 \ + sha256=415c01ce7b157531fdb62686db0705c8a01ac3f6f457423f003211ab3c117b6a \ + sha384=56d4c22b10deb79e54446b8c191b9cf1f626b40b7a378ed28e5966f888bb2ba722de36f3d7472b2f59aba1e9aebfe315 \ + sha512=da0e8efdc9a31df4ac08efb81f9a690034383d6d6aeeb3cfab4148260b462a09d1f4138df3dddad3cc67c2d992f0dfb60fbc9b933a07e102922ad72c77897d51 + meta.db-wal size=3547352 time=1533892857.0 cksum=622514369 \ + md5=2d4a9ad980b7fc48abd801f5225b37e7 \ + rmd160=a5c3af7e225f1bf1c83a957d70fdc6cffe4b09c7 \ + sha1=10e502c8456f316a4f6543d28ec5db555f05305d \ + sha256=38612027d02a0745213e7788dfd0d56ba1940e6d2000b7edf1f96b8905fa2131 \ + sha384=bed2e959f6083ce69cbef438144bd816203da969a81a18f1da1fca40742003aacec78612d3030ada37954d0fe5e58037 \ + sha512=0dc230875e330ce2a6a7f82828f50afb415bcd7660423d7157735151512da35f01f6752023362f79477d2a82ebad2f193bcea642af62ea643a5cabb1acd9c74b + ontologies.gvdb \ + size=361550 time=1533892840.0 cksum=435789844 \ + md5=efb2f7e13f29218b07b16a8de9c87019 \ + rmd160=882bc13fa4bd74f6fddc8d651587837025034619 \ + sha1=1dfc7af25038bdad77844cee09f5fe2d98bd0f2e \ + sha256=ad08ae37ce86bc1da403eb2fb5f4da2842c49e5c94fd5ab4cee0c79adfdf7247 \ + sha384=ba5634e516dbc96d6fc78b0e105a2d942dad5449aad10397d69a43f8bf05378f23b7143916873d9a3db94edf6b055e18 \ + sha512=c5ebbbf4b51d63285a213e4da8ef470c970fac6632b8cd61eb536dd6b3bb95426cd184d0c796d1fac16784780bf469d5b23203616a45cda4d91a896b6baa0c2c + parser-version.txt \ + size=22 time=1533892201.0 cksum=185235860 \ + md5=72f18e39b0136754db8b29489359dcab \ + rmd160=007578237bf335dad7a089d349cc7554fb224e15 \ + sha1=5c6664dbdacf5b7d03baed460aedf810e55cd5a3 \ + sha256=f28a57f94a52411d9dba2cc5ca3bcc289f724a5af4d574d4d34ae8b3631abaf9 \ + sha384=ab2b2e1223e8e01929530869288dc0f4a0b5c25698440371fcf45b807ea08c5371d31131299f42694543869fd792bf29 \ + sha512=7a3bd4530d71dfd19608d3e3e519cb1f89774c3a998a1a8cb3797338a71ff8eadd8f741574e1e465fe018afbcacd2b3fad8d66024551a1ffde54041e9528784e +# ./.cache/tracker +.. + + +# ./.cache/vim +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +vim type=dir nlink=3 time=1533887303.0 + +# ./.cache/vim/swap +swap type=dir nlink=2 time=1533887339.0 +# ./.cache/vim/swap +.. + +# ./.cache/vim +.. + +# ./.cache +.. + + +# ./.config +.config type=dir nlink=9 time=1533892348.0 + monitors.xml \ + mode=0644 size=560 time=1533892348.0 cksum=2260937204 \ + md5=12fd02df41ff6d000396eca039ae2a28 \ + rmd160=e6754f40a05cd5c839b11c70db38d2551ab2da24 \ + sha1=1a4e8a8b49d0fdb1897600848f54dd2cf4ccdac7 \ + sha256=9516d2ab6121e7c120dc43d37370ac48ef461143bd5ed078a54e36e81b88240a \ + sha384=77f51944f2289e23153b6164655820cd7d5c6b60be06b611004ec319ed73fed4794375b4024f169405b3c588c98af43f \ + sha512=c9a6d1ad46e383a2829bb72ffe2e7e76e004e575bd6b44e611d122a018bcec07181e389185052b8b2fe46fc6d77e6bd9ffbac05e62ec34b38852eba4130c7fa1 + monitors.xml~ \ + mode=0644 size=560 time=1533892323.0 cksum=3824915986 \ + md5=37c1b73559d167d795edd4c68c221415 \ + rmd160=28e83036c6c24787ec93cc362edd35df554bb63c \ + sha1=f3e8be7b1f5203ca3e16e7f242a2eed288a0a2cc \ + sha256=e06163e52e831fcb2953099214b45ea3d01fbb08e8e33b62dc4f5482f09a975b \ + sha384=eee195965cb87616aa6132ea345ab2c1e8491114fd45a653a481d3e9b1f7b14aed439acdccaebb7402129acef3fdbd84 \ + sha512=b7db2c3a6ea7fe0260de4800779ce84fbc09b5c0f2e8486bcea01d89615057c738be554974c22d573a50c0a3d26b810fcf806b8324d574c84baaf0f0cb644047 + user-dirs.dirs \ + mode=0600 size=633 time=1533892142.0 cksum=3785135043 \ + md5=e96ec7012d30be266ba6e6d81e88228d \ + rmd160=72e84e6401c96bea6b05e890722daa287415e748 \ + sha1=7da04e08c59f32b0c631dfd59f72496f6d7c6f02 \ + sha256=6a0a498fde15c3747b508f4464e82f53e16c501a49f2e0c9fb114bbfbb051887 \ + sha384=ecc53b3b1f7a8e319d309301c4a2d82f5cba2b6190b454d1afcca175df5197204170a6344aacabdf46860b7e8c235b8b \ + sha512=d12408ad419e7cadba1d6a51727f2bb82c566375140f3a9d71c086eb426266438d4e84b85132f587989c7a66f0fcd7701995c3041eb99288d75339ddc8434af0 + user-dirs.locale \ + mode=0644 size=5 time=1533892142.0 cksum=1549138745 \ + md5=71095c56c641f2c4a4f189b9dfcd7a38 \ + rmd160=3252f20d06d10500a638e6b10764bf126d7bad65 \ + sha1=fa73905e89c5c67747184ab362f7d0d86a939eca \ + sha256=674ac4b0bd40c5764a1ddf4e0119e9b4ee02f743211eefcf7b27c932a4831a4b \ + sha384=732874891284dd86a6df3a16ee10461fe85e7e1194a65d9f4f14fa90a6e61312d5c47d7b1a58408ca490d1a4e50a04ed \ + sha512=b6f5072c9cb9a2c14962464beda070f272996577892afacb000903798ebac4e422d660722959bce95a6e5f54ed6ca09308245c82716dad76bfc15ff8b3b4dd36 + +# ./.config/dconf +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +dconf type=dir mode=0755 nlink=2 time=1533982902.0 + user size=3748 time=1533982901.0 cksum=598808774 \ + md5=f2772e8d168e4175fcef76c3050f7131 \ + rmd160=01389b32b9ef1292769a58bc0669ddf9d6900101 \ + sha1=9a083aa67a95dae389563f1e3a9883b7e0943fd9 \ + sha256=2fe41b3fc1342550cad7b124f292b20ee621b80ad6d93adf2c5e32cd44be0ceb \ + sha384=cb2cc1929d983dd836fc8fa915d8af3238953ebc0b7b3297987670ef31a746ffd524ba6bd7056187b48ae0cd45655058 \ + sha512=83bec9ddc39b481c8451011914f2dd18ebbf49b506e1277ddec50ca5a7b5fab89203cf98ea1433ccc08f5b4be3bcf5c0de0a6fa5391845603740589c432e6c2b +# ./.config/dconf +.. + + +# ./.config/evolution +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +evolution type=dir nlink=3 time=1533892198.0 + +# ./.config/evolution/sources +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +sources type=dir mode=0700 nlink=2 time=1533892198.0 + system-proxy.source \ + size=993 time=1533892198.0 cksum=3992040821 \ + md5=32746f7a7be67f745a4101e22022d7f3 \ + rmd160=edc2a33cea302cbaaf6502b381f07e8991971080 \ + sha1=1e1e573c8b472cba64ce6b0646c743cd16dcfeca \ + sha256=1e9d2f3fbee1cd67b58660dfd8945d2e60d72824db1561645b7d35581b8f224e \ + sha384=8a20dee35758eccefbaa91608ccda52944c2a8ee61d741b2d653fbf70cca27851e0f350b7323a0e6fc45f2893db7c71a \ + sha512=34c2f38620ad90d2ec28f01b0f7ef0427a8c4cfe23ddb411f0f22be3774ffb939bc1a6963e1bd364f8b0ed71c0eda3ba1b8fb2d1e9ee1dad9c4d3438b2f8235a +# ./.config/evolution/sources +.. + +# ./.config/evolution +.. + + +# ./.config/gnome-session +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +gnome-session type=dir nlink=3 time=1533892197.0 + +# ./.config/gnome-session/saved-session +saved-session type=dir nlink=2 time=1533892197.0 +# ./.config/gnome-session/saved-session +.. + +# ./.config/gnome-session +.. + + +# ./.config/goa-1.0 +goa-1.0 type=dir mode=0755 nlink=2 time=1533892198.0 +# ./.config/goa-1.0 +.. + + +# ./.config/gtk-3.0 +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +gtk-3.0 type=dir mode=0700 nlink=2 time=1533892840.0 + bookmarks size=127 time=1533892839.0 cksum=4055419018 \ + md5=b70ea27a8be6be1941a23013a970e1b0 \ + rmd160=1d68b8e898620d131b2820eeba02b49b0db756d8 \ + sha1=8587623c8b43874b57a570528e93066d3178a9b1 \ + sha256=215e70d99350f64ffb6bab171b418b1ee620e5d728a098534fe87963018d4263 \ + sha384=6af12d518cbcb1b8bf0707558ee490b51489298d80516626d2c9ae3c4d9479f89f5bb648ecde3b0f4db3eca028738b48 \ + sha512=9c75a71c2ba2355e39cedf2c0993ffc22e6f9af211336e8295e78487dda7734ce1fe29d8678b772aa6fd53696ce76375d42b8169b378f9381e5e505166244364 +# ./.config/gtk-3.0 +.. + + +# ./.config/ibus +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +ibus type=dir nlink=3 time=1533892198.0 + +# ./.config/ibus/bus +bus type=dir nlink=2 time=1533892198.0 +# ./.config/ibus/bus +.. + +# ./.config/ibus +.. + + +# ./.config/pulse +/set type=file uname=bts gname=bts mode=0600 nlink=1 flags=none +pulse type=dir mode=0700 nlink=2 time=1533892202.0 + 0054cfa0224449deb05473622504e07f-card-database.tdb \ + size=16384 time=1533892197.0 cksum=349238371 \ + md5=bbb103738cb7384a5464259154ebf7f1 \ + rmd160=4d17543538ea5b84ea11c7e62851a65f3f394c26 \ + sha1=9c3ae2b3c9ddf55bbf7a4480b89dde866b430e74 \ + sha256=7fd739b58dae5b867391cdb71934e57d1628f2c0a69865a24ab0d39d75923c27 \ + sha384=2450c74418b0c343efdfafe220a099f949cec0124435e15bde3fdf8d22f888229d89b60d1782bdb723d474005ea565af \ + sha512=2eac5a6eef1a51f14cf40b050a22057f3d32c9a308aef67bbd0e168100897baaa953167020b96a4b14b3b0307b578c9b98b0feb4edcfb75e9f853505f6a71389 + 0054cfa0224449deb05473622504e07f-default-sink \ + size=1 time=1533892842.0 cksum=3515105045 \ + md5=68b329da9893e34099c7d8ad5cb9c940 \ + rmd160=c0da025038ed83c687ddc430da9846ecb97f3998 \ + sha1=adc83b19e793491b1c6ea0fd8b46cd9f32e592fc \ + sha256=01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b \ + sha384=ec664e889ed6c1b2763cacf7899d95b7f347373eb982e523419feea3aa362d891b3bf025f292267a5854049091789c3e \ + sha512=be688838ca8686e5c90689bf2ab585cef1137c999b48c70b92f67a5c34dc15697b5d11c982ed6d71be1e1e7f7b4e0733884aa97c3f7a339a8ed03577cf74be09 + 0054cfa0224449deb05473622504e07f-default-source \ + size=1 time=1533892842.0 cksum=3515105045 \ + md5=68b329da9893e34099c7d8ad5cb9c940 \ + rmd160=c0da025038ed83c687ddc430da9846ecb97f3998 \ + sha1=adc83b19e793491b1c6ea0fd8b46cd9f32e592fc \ + sha256=01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b \ + sha384=ec664e889ed6c1b2763cacf7899d95b7f347373eb982e523419feea3aa362d891b3bf025f292267a5854049091789c3e \ + sha512=be688838ca8686e5c90689bf2ab585cef1137c999b48c70b92f67a5c34dc15697b5d11c982ed6d71be1e1e7f7b4e0733884aa97c3f7a339a8ed03577cf74be09 + 0054cfa0224449deb05473622504e07f-device-volumes.tdb \ + size=12288 time=1533892197.0 cksum=178495308 \ + md5=ba1d50b02007978aacdcfdf2d6124820 \ + rmd160=2befe1a2d2680bbdfb3f322fcbee82a56df983ba \ + sha1=8bffe1e59ee2ac8677eab209c2a382115b5b8dca \ + sha256=ded42ab1133ac7a4b4c355dffe2647158037e25ade23f75f6d2288dd5dd3c38a \ + sha384=4dfd013ccf94eb25b67a28ed2e63d903e644a0515d7c03037df34289e3fc02110dabf4ca6cce9d3093f9f9f95d186ec9 \ + sha512=e778d74b7ba7cf23fe16c7a0a27851169a314f308308bc5ac86480160c24fc8e0c0d83222a3803adaa13f1e2d2142093924207b3c3dc6398e4cd9c765b3953d7 + 0054cfa0224449deb05473622504e07f-stream-volumes.tdb \ + size=696 time=1533892197.0 cksum=2603747815 \ + md5=734dcdfd150ed504d29ed536dce5dfcf \ + rmd160=7f26e4a77d8dac9ef2bc96cccd96cb0560528e4d \ + sha1=647e3a6af34d93743e018fa5aab66aeb2f716d8f \ + sha256=4579a11c1145ee206796c2f5326d8ee86822c865cbb7a9503be153764526df1a \ + sha384=14cbb117f335703cb8d12b75ed8be68c00b3271a333904ef8edfe07c4862deeb72af86a6dcb2a0bda041ae974568c280 \ + sha512=a5c9f98717cecffa7ef78ac023f029c349a34496e08d6582d64476879ca5b434a66dfcd4c250bc1e3c7e610a29d18c44934dd78d38f8f89989393dfd41706783 + cookie size=256 time=1533892197.0 cksum=120835636 \ + md5=325fd991f11a7fcad7dc8e9ebbf036fb \ + rmd160=1ad702cd20eaebdfbc7ccf9aa4de2bf304b1dd77 \ + sha1=a4a950a431bbd14ecc52183a8d89877ca6f70d26 \ + sha256=530f4ac831c83d95b5438b7e52e3980504a90df845056ff5899635d2c2c16af0 \ + sha384=7e8c83c223a1124c46a6066a01ecca09e93fa2da28e006d205490d56a836d447d5bd67dd42f1fee702a1376736b054a4 \ + sha512=949f143e9400d29f481ef5dbba6a421bcf7d138828407761b06378ca3988a424c118f4a34ddc6dd2dbf71c553911187c0090295bf9bf86a361aa78343a2a7dce +# ./.config/pulse +.. + +# ./.config +.. + + +# ./.local +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +.local type=dir nlink=3 time=1533892197.0 + +# ./.local/share +share type=dir nlink=14 time=1533892204.0 + +# ./.local/share/app-info +/set type=file uname=bts gname=bts mode=0755 nlink=1 flags=none +app-info type=dir nlink=3 time=1533892204.0 + +# ./.local/share/app-info/xmls +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +xmls type=dir mode=0755 nlink=2 time=1533982904.0 + extensions-web.xml \ + size=1111654 time=1533982904.0 cksum=2919485898 \ + md5=7040c7957671ed9cf0ab832f0be03ecf \ + rmd160=b0e772249f30fb8b5280dc12a689ec2052c45853 \ + sha1=7f47eef68d49faec413a4f96fa768fa27328a420 \ + sha256=a00a65acaf4cbe6967f20efc2f214c278c5d3dbe5fab1a5189e06ba941de54df \ + sha384=d4bf2143cef6086b405b631912f03c0da2bfb939ff14521256066054667aead8be383b5d70a2928ec4dcad6fdf8d1ec9 \ + sha512=e660ab87b556d5205aa6c9bb28582c84c0cb3182c8d99c8292a439cb0e024be09365fb97a25e7e6fe94f89e3b7961372e3c9d9f889f211198588a068a91a5e3d +# ./.local/share/app-info/xmls +.. + +# ./.local/share/app-info +.. + + +# ./.local/share/applications +applications type=dir mode=0700 nlink=2 time=1533892199.0 +# ./.local/share/applications +.. + + +# ./.local/share/evolution +/set type=file uname=bts gname=bts mode=0755 nlink=1 flags=none +evolution type=dir mode=0700 nlink=7 time=1533892198.0 + +# ./.local/share/evolution/addressbook +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +addressbook type=dir mode=0755 nlink=4 time=1533892201.0 + +# ./.local/share/evolution/addressbook/system +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +system type=dir mode=0700 nlink=3 time=1533892840.0 + contacts.db size=86016 time=1533892839.0 cksum=3427469314 \ + md5=e6375479e5e41a20968232d3188d9562 \ + rmd160=71740e3d3ac9be7d419dd58686ab1fcde32ba62e \ + sha1=b69eb6fa06680d8221d727909d4e70baeeb82bb5 \ + sha256=fd52acdd98541d49090ff5f37c52160b445d9340ad0fbe7eef8b71d273368a41 \ + sha384=a4d86b5d88021c7750092a9a8f7795163449b11e2dcd54461f2e231fca91c8c84e4be3beb695cdf7efc19f879ebf03f3 \ + sha512=619346f552799642cb12987caa097e4921e1132c71163f2cc65f14f21c5e43b751e518f28ffa49d29c1d94e25e74be1528797eb43ea286598df74257cc619ccc + +# ./.local/share/evolution/addressbook/system/photos +photos type=dir mode=0700 nlink=2 time=1533892204.0 +# ./.local/share/evolution/addressbook/system/photos +.. + +# ./.local/share/evolution/addressbook/system +.. + + +# ./.local/share/evolution/addressbook/trash +trash type=dir mode=0755 nlink=2 time=1533892198.0 +# ./.local/share/evolution/addressbook/trash +.. + +# ./.local/share/evolution/addressbook +.. + + +# ./.local/share/evolution/calendar +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +calendar type=dir mode=0755 nlink=4 time=1533892201.0 + +# ./.local/share/evolution/calendar/system +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +system type=dir mode=0700 nlink=2 time=1533892201.0 + calendar.ics \ + size=173 time=1533892201.0 cksum=1042167468 \ + md5=b652f0f649ff8d20184fa6e8ea0c308e \ + rmd160=f2af8eb58fb0fe98d0c501fccee88cb59899171c \ + sha1=eb5a0cc61686e356a451d926131c6bc3a098fb5f \ + sha256=e0b677c7cc97564f27f3175fc120fc7321b67af078ff2aeb90c32f85586b1abf \ + sha384=97e392aebacd84c6f575a2e4c6b0a6e0587f8703a84b8769092a5b294f50909b50051659f339039adfc80f0250246487 \ + sha512=91f6ba15ab02d48659b5b31596ad966f9369fe1d16b831a501b34c238855a0a612cf40972c578eb8b231b5aec09452698d32db662121d6f0f8058c9f4c262ab8 +# ./.local/share/evolution/calendar/system +.. + + +# ./.local/share/evolution/calendar/trash +trash type=dir mode=0755 nlink=2 time=1533892198.0 +# ./.local/share/evolution/calendar/trash +.. + +# ./.local/share/evolution/calendar +.. + + +# ./.local/share/evolution/mail +/set type=file uname=bts gname=bts mode=0755 nlink=1 flags=none +mail type=dir nlink=3 time=1533892198.0 + +# ./.local/share/evolution/mail/trash +trash type=dir nlink=2 time=1533892198.0 +# ./.local/share/evolution/mail/trash +.. + +# ./.local/share/evolution/mail +.. + + +# ./.local/share/evolution/memos +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +memos type=dir mode=0755 nlink=4 time=1533892204.0 + +# ./.local/share/evolution/memos/system +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +system type=dir mode=0700 nlink=2 time=1533892204.0 + journal.ics size=173 time=1533892204.0 cksum=445655860 \ + md5=8197679d5902039270b1d41880df5131 \ + rmd160=d7d673eac482758c3e9ccd9297e8d22adafcfe34 \ + sha1=13dc8889710389e4a1869b83e6e149b56946a19f \ + sha256=0f703f467c8b974c8353f45d7dcd658329f6f9049ec881f7c9d536e5abc5d9ca \ + sha384=a49a2a17598592a1d5bbf8e1d17950cf394f8e38edffb95484515020838475a8fe21222ed8c80c508bd18b6e2ca529d7 \ + sha512=9329033ba290499adb51c2e6fe68bda2c7614d26ec6dabb7fdcd09fe0db4953eaa18b17830bd9ec94f267f4861bca6a19b1d1567b8ef861a52f81324f349cde4 +# ./.local/share/evolution/memos/system +.. + + +# ./.local/share/evolution/memos/trash +trash type=dir mode=0755 nlink=2 time=1533892198.0 +# ./.local/share/evolution/memos/trash +.. + +# ./.local/share/evolution/memos +.. + + +# ./.local/share/evolution/tasks +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +tasks type=dir mode=0755 nlink=4 time=1533892204.0 + +# ./.local/share/evolution/tasks/system +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +system type=dir mode=0700 nlink=2 time=1533892204.0 + tasks.ics size=173 time=1533892204.0 cksum=3068391378 \ + md5=c1912ed5ffc3a459d7a72d29a906c446 \ + rmd160=e479659f9da3792f3f86f53eabe346c26676281a \ + sha1=f20ec7cd98f80d515ae05c540493103d01a67943 \ + sha256=ec4f325c66914205e513c4822a813bd0e15dd6f216916ecb9acd9a4f45e2f8c4 \ + sha384=fa03a614501c2886bd9548e076fa8f33fce70b3c7ada5a16e0d9977a8ba024f522be9597429b71a446b6f1a188b3941c \ + sha512=708ec3644008054f4b99c0ceda08bdad226dafefbd419caa390b459f8bcc725ebc78320816a3782e65b145b8e7ebe2f90f8514193d2d6cd84b742cbabcfb46e6 +# ./.local/share/evolution/tasks/system +.. + + +# ./.local/share/evolution/tasks/trash +trash type=dir mode=0755 nlink=2 time=1533892198.0 +# ./.local/share/evolution/tasks/trash +.. + +# ./.local/share/evolution/tasks +.. + +# ./.local/share/evolution +.. + + +# ./.local/share/flatpak +/set type=file uname=bts gname=bts mode=0755 nlink=1 flags=none +flatpak type=dir nlink=4 time=1533892202.0 + .changed mode=0644 size=0 time=1533892202.0 cksum=4294967295 \ + md5=d41d8cd98f00b204e9800998ecf8427e \ + rmd160=9c1185a5c5e9fc54612808977ee8f548b2258d31 \ + sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709 \ + sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ + sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b \ + sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + +# ./.local/share/flatpak/db +db type=dir nlink=2 time=1533892201.0 +# ./.local/share/flatpak/db +.. + + +# ./.local/share/flatpak/repo +repo type=dir nlink=7 time=1533892203.0 + config mode=0644 size=67 time=1533892202.0 cksum=4094986651 \ + md5=f71211794058ee7a86c759209a98d627 \ + rmd160=a2560445cc0ce2233f536170c0306bd4ebdc59f3 \ + sha1=ebf213b5dda387a444233db3aedfade13c07029d \ + sha256=2c42847170b4975de955e4149a5e0737babe5c5d4951d42a7a960e73c18528ea \ + sha384=aa3a68b6ec0e10c9e8a04952fd1959f26932f4d3bb6298bde9d6914def1b40ad6fc09f0da3b5553c5328bc758a2ae393 \ + sha512=e5d8b958e787b155663bedb7dc418b828c0b0f4791559b65125a492c0a8958d8b265d41cd1b209291d4116a84d639636185906b4695655ecbb70c79904ed6f61 + +# ./.local/share/flatpak/repo/extensions +extensions type=dir nlink=2 time=1533892202.0 +# ./.local/share/flatpak/repo/extensions +.. + + +# ./.local/share/flatpak/repo/objects +objects type=dir nlink=2 time=1533892202.0 +# ./.local/share/flatpak/repo/objects +.. + + +# ./.local/share/flatpak/repo/refs +refs type=dir nlink=5 time=1533892202.0 + +# ./.local/share/flatpak/repo/refs/heads +heads type=dir nlink=2 time=1533892202.0 +# ./.local/share/flatpak/repo/refs/heads +.. + + +# ./.local/share/flatpak/repo/refs/mirrors +mirrors type=dir nlink=2 time=1533892202.0 +# ./.local/share/flatpak/repo/refs/mirrors +.. + + +# ./.local/share/flatpak/repo/refs/remotes +remotes type=dir nlink=2 time=1533892202.0 +# ./.local/share/flatpak/repo/refs/remotes +.. + +# ./.local/share/flatpak/repo/refs +.. + + +# ./.local/share/flatpak/repo/state +state type=dir nlink=2 time=1533892202.0 +# ./.local/share/flatpak/repo/state +.. + + +# ./.local/share/flatpak/repo/tmp +tmp type=dir nlink=3 time=1533892202.0 + +# ./.local/share/flatpak/repo/tmp/cache +cache type=dir nlink=2 time=1533892202.0 +# ./.local/share/flatpak/repo/tmp/cache +.. + +# ./.local/share/flatpak/repo/tmp +.. + +# ./.local/share/flatpak/repo +.. + +# ./.local/share/flatpak +.. + + +# ./.local/share/gnome-settings-daemon +/set type=file uname=bts gname=bts mode=0644 nlink=1 flags=none +gnome-settings-daemon \ + type=dir mode=0755 nlink=2 time=1533892200.0 + input-sources-converted \ + size=0 time=1533892200.0 cksum=4294967295 \ + md5=d41d8cd98f00b204e9800998ecf8427e \ + rmd160=9c1185a5c5e9fc54612808977ee8f548b2258d31 \ + sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709 \ + sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ + sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b \ + sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e +# ./.local/share/gnome-settings-daemon +.. + + +# ./.local/share/gnome-shell +gnome-shell type=dir mode=0700 nlink=2 time=1533902567.0 + application_state \ + size=202 time=1533902567.0 cksum=935551653 \ + md5=bd4d6ec5e9732cb1f29355cf83eef95c \ + rmd160=fa417fec73cf18de7e61b46e1a4dcac60a5a3269 \ + sha1=ab0d453c4b7eecc353252bcb57eaab7de2a08ac1 \ + sha256=c6bb9db54d5ce81e7b84078fee1d319ae68bd1d09b6a67410b7ac2d9b35b812e \ + sha384=1ae45c3d448a0d67e99e4cf09826f7e3ed0fab3d5d7084e4bb212b7b74d0ee626dcd9c1f4f294a34059cd756a50f499a \ + sha512=fd28018329d66d856ea1647a396938d266bd1b61bb146b3c089a059a162ee43d48a7754f6bbb87943a0bdb364bbf5fa7335d874241991ebf2a02547beff52083 +# ./.local/share/gnome-shell +.. + + +# ./.local/share/gvfs-metadata +/set type=file uname=bts gname=bts mode=0600 nlink=1 flags=none +gvfs-metadata type=dir mode=0700 nlink=2 time=1533892264.0 + home size=64 time=1533892263.0 cksum=1303115355 \ + md5=44256d920996ba3bc93084ab63b56e23 \ + rmd160=e27207934cc7294857ba609ac057c7e362b52f4c \ + sha1=2ed26009aee5ce04cab56799aed84989114cd677 \ + sha256=92cb4e85a26044194d895dbed75bc65910032a2bdca35a51b4ae71914045ad5a \ + sha384=f242c1cb662b46196cb18640f76188e4e75d9a7888dcf0bec9f3e6e1f6b8176e3c636a92b77da415c298d0da6d381730 \ + sha512=c194ae6dbf3b2afca379b85e1dfa6204307324764ced1523ed4acc878a63fe8af8835b00d138c1e0f02535c9e7bb5c21979580ad181512223ddbc52c785fc5de + home-0e821ed7.log \ + mode=0644 size=32768 time=1533892264.0 cksum=4129138632 \ + md5=9e1aeb85185e06fbb563a4f86c51c531 \ + rmd160=02005dbb5bde6a26a5fb8934f0e693937f53521d \ + sha1=8191b24587cea4f40be8e5f71ff9b7dff46e615c \ + sha256=22fe3fe144f4db1bd37c9932a455126ba0fa3dfc25a4dd1c73ab621e52453a30 \ + sha384=1d9eccc897f301804d93084127293a233ee371c0c071aadee0dba47914b72d657bc9fa6fd8da635d452b78ec198a17ca \ + sha512=8a1e553648f4f8277f355a8e54f81b18994dbd270b79a6690e844091723cb17fd1ee4f5229442f2a7500d2b76ca100299c36774cfa1b370d4cfaa9df79790369 +# ./.local/share/gvfs-metadata +.. + + +# ./.local/share/icc +icc type=dir mode=0755 nlink=2 time=1533892200.0 +# ./.local/share/icc +.. + + +# ./.local/share/keyrings +keyrings type=dir mode=0700 nlink=2 time=1533892197.0 +# ./.local/share/keyrings +.. + + +# ./.local/share/sounds +sounds type=dir mode=0700 nlink=2 time=1533892199.0 +# ./.local/share/sounds +.. + + +# ./.local/share/telepathy +/set type=file uname=bts gname=bts mode=0700 nlink=1 flags=none +telepathy type=dir nlink=3 time=1533892198.0 + +# ./.local/share/telepathy/mission-control +/set type=file uname=bts gname=bts mode=0600 nlink=1 flags=none +mission-control type=dir mode=0700 nlink=2 time=1533892198.0 + accounts.cfg \ + size=21 time=1533892198.0 cksum=3539680592 \ + md5=b00b5be521f54486ca642da7793ee116 \ + rmd160=034d037b8dd84bd6c6ae86d7f7cbcd265d77f04c \ + sha1=1db7e500fc14542d2cf007150d8edc6778a11536 \ + sha256=c0f86d64fa4389f0dac8df955a5bd252757e74075e3d40ee7cf0299d27df4141 \ + sha384=9169a7900ee434feeefa13703794a39576b7a3f54ec87618e12e549d9fcf5b05f338591085a58c8a4116cc4630354673 \ + sha512=d853b5b73b5fbefbedf4ca0ede90a3a019208eb7050cfe1ea7f3f416d2f9d0cea0001122ba8a118a8ce4496611c2e5d7cfe3ed56fbb5d2a086b3225619b0a74c +# ./.local/share/telepathy/mission-control +.. + +# ./.local/share/telepathy +.. + + +# ./.local/share/tracker +/set type=file uname=bts gname=bts mode=0755 nlink=1 flags=none +tracker type=dir nlink=3 time=1533892200.0 + +# ./.local/share/tracker/data +/set type=file uname=bts gname=bts mode=0640 nlink=1 flags=none +data type=dir mode=0755 nlink=2 time=1533892839.0 + .meta.isrunning \ + size=0 time=1533892839.0 cksum=4294967295 \ + md5=d41d8cd98f00b204e9800998ecf8427e \ + rmd160=9c1185a5c5e9fc54612808977ee8f548b2258d31 \ + sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709 \ + sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ + sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b \ + sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + tracker-store.journal \ + size=115355 time=1533892857.0 cksum=1685274014 \ + md5=c361129af8e96bf726d041538bfce95c \ + rmd160=92383ec12dbc1e27b54b87696e05fbf6896f17ef \ + sha1=af3d0e6438ba7af452c3d341af65ec144b48adb4 \ + sha256=897f648061b2b9b13191d5e93303a4deb75d8f4f8907c3e297b8bd73ed481562 \ + sha384=867d89a809a505e9739ce73d8f3af3d754fc8bdc1e865fa80b79c3f352ca8805d93210b6875a39f24c66c1bee1ba7e36 \ + sha512=4759a3c82cc4624fabb05966ccc7ec615bf50ba54530ff09b4d2ef472f6489d8e179751ab201512dfc3e137686d706470dc71a8e2569a125097a763b864f27f0 + tracker-store.ontology.journal \ + size=75541 time=1533892840.0 cksum=3283930654 \ + md5=7ab9783b7c36f6bb2efb71fbfc2db9e4 \ + rmd160=57c2fe3aa04119a48ed9fedeecd7526f7e51b9e2 \ + sha1=334017fa833b7bd440af20d7cc02f2a478622ff7 \ + sha256=a593460b51b74e120c443facbf3fc9c0d0c77b8bcd6c93005b445ef5641fbb04 \ + sha384=6dc06e8be20eac02d54efdd1776e0cf937f2f066c2f5fca3d7e02033457e36b718204cb678ded8f54ab194cb551be404 \ + sha512=2021a007392f09ff24ccdbe56f82b011bfdb849bb8e214d336ec98d6353bc5d0fba0b1c50c357caa151b14afa67f45ae5119290748d928dfaf7620af10c68298 +# ./.local/share/tracker/data +.. + +# ./.local/share/tracker +.. + +# ./.local/share +.. + +# ./.local +.. + + +# ./.ssh +/set type=file uname=bts gname=bts mode=0600 nlink=1 flags=none +.ssh type=dir mode=0700 nlink=2 time=1533887570.0 + authorized_keys \ + size=9040 time=1533887408.0 cksum=2601383629 \ + md5=ef306154ae6fd310e49e73f8d7dc2a0c \ + rmd160=22ca6d5a6e05bad7c5b98da847291d86c7c86d5a \ + sha1=25e550ca6450830b6597b8714fa36a3b742ea937 \ + sha256=31d9b33468ef0752cc60aa3103d1313bfe44e80a20091ce935dd47a8e469f518 \ + sha384=63b403a369b216d4f4a8cee01cde1a54270a11c60363b5cafe787c4f791601a70bc7bbaadc25215d8c7159fbdc7401f0 \ + sha512=70ae05aed2ec80896ae2284db674bd8e521053616b1b231502befb5286a717c73ba6af8a51fb79ac5252fcac9350f01a19e5501b7780cec7e46e624636c831e6 + id_ed25519 size=399 time=1533887539.0 cksum=3390307129 \ + md5=7c8e5fb99704b31d047fbbd05367dd16 \ + rmd160=7bd2c53596ff7bda12b09fb3ba030e72aa9a2fd1 \ + sha1=5747e6565056c0701799efceb3c2d1a245876f4b \ + sha256=b3d51c23e852f11c55e584269f961424c7f81cc49e8d6560e99d566804d3a84b \ + sha384=4a849c2ef55bc30267c72b0ad0dcaf21398f6617862d5eb6bc0ba3fc415bff407662fc956161dc1c828e98af2466470d \ + sha512=b40422f4c203ad85b8ff5c043d1dd1c6639cb6fbb2b8dcb9e4aff622bcc75d4a978654ed1c39f36c8ec65954f508eab740c6596ac117b7c233bf8036f0e09cc9 + id_ed25519.pub \ + mode=0644 size=93 time=1533887539.0 cksum=4046864085 \ + md5=5a68c5c7afdd393f84cbdbe192810b87 \ + rmd160=bd2335f8450d88747088c74c27d38015e2e3527f \ + sha1=efee6435defa70bc61f8fe9cb4bbd2b80d4d2320 \ + sha256=eaf28e30061448b2d20228913de13000617cbc3f9697ab8a5d19a739e0032fbb \ + sha384=7b706827a4d35d1b89d8ec43cc669fc43f8ac6e52f28eb382f6b93c00ee44e4aac651b742d44a7c383c28f25e9bd437d \ + sha512=5d8fb45499da9200284a8224c2ef04d19aaecfb7ac2edcf7a70a10cc6b5b964b1c30197a12c7b893192eeb7f75c3c24dc107fad9dcd19d607f8408703721f962 + id_rsa size=3381 time=1533887570.0 cksum=1765081168 \ + md5=304140c5d624d31330565dd3f3d26350 \ + rmd160=2f76278c026e39af4722a2359d10e52b95f5b192 \ + sha1=c02fd05b2e717f500130003dc316da00c61726d4 \ + sha256=ac50a2821bfc0c5d72232650e34aab5849c20d4b41958a0b83d2191c788ecbad \ + sha384=a397b3100bf4df5a4fcae4de1e7e6a0131db5f31fd8654dd3e12496753c61ed3a7b82fecef0b0d9f2083e14ed0a6015c \ + sha512=72600d3fc7c22aa268f412d5e49c91915f068a173126523f01778863a56a9d0bfb7717710c3a4f4f54be86dcffd09ce639661fb816a5a5bfd31c82044439b8bf + id_rsa.pub mode=0644 size=737 time=1533887570.0 cksum=3314663523 \ + md5=7e14b79d2080b8606a11f3f074faeed0 \ + rmd160=c598f851ddecc7c1f5434b57a57e24d1152caf14 \ + sha1=95191fe37fbf1c2dba188cf5d540156621b658c7 \ + sha256=628e594c229a74206b11af147f98e1a7231ce70f29a07ba76f6f50dbbff0c3b5 \ + sha384=e3464a513c89e2034f806f61e8b66f3af4d73b95467fda41941de23dd8eeaafb6e6ef523e8ee2459145d0a88896b105c \ + sha512=51a0adb421cf48cd6532dce8fef745736eb15d634ebe04968d80f6e86edf201196180121589fdbbd285d2d1532ca7708a7fde50a620bd43cf01d3bd1d9dc034d +# ./.ssh +.. + + +# ./Desktop +Desktop type=dir mode=0755 nlink=2 time=1533892142.0 +# ./Desktop +.. + + +# ./Documents +Documents type=dir mode=0755 nlink=2 time=1533892142.0 +# ./Documents +.. + + +# ./Downloads +Downloads type=dir mode=0755 nlink=2 time=1533892142.0 +# ./Downloads +.. + + +# ./Music +Music type=dir mode=0755 nlink=2 time=1533892142.0 +# ./Music +.. + + +# ./Pictures +Pictures type=dir mode=0755 nlink=2 time=1533892142.0 +# ./Pictures +.. + + +# ./Public +Public type=dir mode=0755 nlink=2 time=1533892142.0 +# ./Public +.. + + +# ./Templates +Templates type=dir mode=0755 nlink=2 time=1533892142.0 +# ./Templates +.. + + +# ./Videos +Videos type=dir mode=0755 nlink=2 time=1533892142.0 +# ./Videos +.. + diff --git a/external/apacman-current.pkg.tar.xz b/external/apacman-current.pkg.tar.xz new file mode 100644 index 0000000..9291779 Binary files /dev/null and b/external/apacman-current.pkg.tar.xz differ diff --git a/external/aurman-current.pkg.tar.xz b/external/aurman-current.pkg.tar.xz new file mode 100644 index 0000000..d0cc1aa Binary files /dev/null and b/external/aurman-current.pkg.tar.xz differ