+class DirectoryStorageBackend (PlainTextBackend):
+ """
+ I maintain a password database in a directory, with one file per password.
+
+ This makes password databases easy to maintain in a revision-control system
+ such as Git.
+
+ The directory is structured as follows.
+
+ dir/meta Contains metadata, similar to the `FlatFileBackend'.
+
+ dir/pw/LABEL Contains the (raw binary) payload for the given password
+ LABEL (base64-encoded, without the useless `=' padding, and
+ with `/' replaced by `.').
+
+ dir/tmp/ Contains temporary files used by the implementation.
+ """
+
+ NAME = 'dir'
+ METAMAGIC = '### pwsafe password directory metadata'
+
+ def _open(me, file, writep):
+ if not _OS.path.isdir(file) or \
+ not _OS.path.isdir(_OS.path.join(file, 'pw')) or \
+ not _OS.path.isdir(_OS.path.join(file, 'tmp')) or \
+ not _OS.path.isfile(_OS.path.join(file, 'meta')):
+ raise StorageBackendRefusal
+ me._dir = file
+ me._parse_file(_OS.path.join(file, 'meta'), magic = me.METAMAGIC)
+ def _parse_line(me, line):
+ me._parse_meta(line)
+
+ def _create(me, file):
+ _OS.mkdir(file, 0700)
+ _OS.mkdir(_OS.path.join(file, 'pw'), 0700)
+ _OS.mkdir(_OS.path.join(file, 'tmp'), 0700)
+ me._mark_dirty()
+ me._dir = file
+
+ def _close(me, abruptp):
+ if not abruptp and me._dirtyp:
+ me._write_file(_OS.path.join(me._dir, 'meta'),
+ me._write_meta, magic = me.METAMAGIC)
+
+ def _pwfile(me, label, dir = 'pw'):
+ return _OS.path.join(me._dir, dir, _b64(label).replace('/', '.'))
+ def _get_passwd(me, label):
+ try:
+ f = open(me._pwfile(label), 'rb')
+ except (OSError, IOError), e:
+ if e.errno == _E.ENOENT: raise KeyError, label
+ else: raise
+ with f: return f.read()
+ def _put_passwd(me, label, payload):
+ new = me._pwfile(label, 'tmp')
+ fd = _OS.open(new, _OS.O_WRONLY | _OS.O_CREAT | _OS.O_TRUNC, 0600)
+ _OS.close(fd)
+ with open(new, 'wb') as f: f.write(payload)
+ _OS.rename(new, me._pwfile(label))
+ def _del_passwd(me, label):
+ try:
+ _OS.remove(me._pwfile(label))
+ except (OSError, IOError), e:
+ if e == _E.ENOENT: raise KeyError, label
+ else: raise
+ def _iter_passwds(me):
+ pw = _OS.path.join(me._dir, 'pw')
+ for i in _OS.listdir(pw):
+ with open(_OS.path.join(pw, i), 'rb') as f: pld = f.read()
+ yield _unb64(i.replace('.', '/')), pld
+