#!/usr/bin/env python
import os, os.path
import optparse
import xerox
import signal
import subprocess
import sys
import yaml

VERSION = '%prog 0.1.4'
DATABASE_PATH = '~/.passwords.yaml.asc'

# install silent Ctrl-C handler
def handle_sigint(*_):
  print
  sys.exit(1)
signal.signal(signal.SIGINT, handle_sigint)

# parse command-line options
parser = optparse.OptionParser(usage='Usage: %prog [options] [pathquery[:userquery]]', version=VERSION)
parser.add_option('-E', '--echo', action='store_true', help='echo passwords on console (as opposed to copying them to the clipboard)')
parser.add_option('-s', '--strict', action='store_true', help='fail if password should be copied to clipboard but more than one result has been found')
opts, args = parser.parse_args()

# verify that database file is present
database_path = os.path.expanduser(DATABASE_PATH)
if not os.path.exists(database_path):
  print 'Error: Password safe not found at %s.' % DATABASE_PATH
  sys.exit(-1)

# read master password and open database
popen = subprocess.Popen("gpg --use-agent --no-tty -qd '%s'" % database_path, shell=True, stdout=subprocess.PIPE)
output,_ = popen.communicate()
if popen.returncode:
  sys.exit(-1)

# parse YAML
root_node = yaml.load(output)

# create list of entries sorted by their canonical path
def make_canonical_path(path):
  return path.replace(' ', '_').lower()

entries = []

def collect_entry(node, path):
  if type(node) != dict:
    node = {'P':node}
  else:
    if node.has_key('U') and type(node['U']) == int:
      node['U'] = str(node['U'])
  node['canonical_path'] = make_canonical_path(path)
  entries.append(node)

def collect_entries(node, path):
  if type(node) == list:
    for n in node:
      collect_entry(n, path)
  elif type(node) == dict:
    if node.has_key('P'):
      collect_entry(node, path)
    else:
      for (key,value) in node.iteritems():
        collect_entries(value, path + '.' + key if path else key)
  else:
    collect_entry(node, path)

collect_entries(root_node, '')

entries = sorted(entries, key=lambda e: e['canonical_path'])

# perform query
if args and args[0].find(':') != -1:
  query_path, query_user = [make_canonical_path(q) for q in args[0].split(':')]
elif args:
  query_path, query_user = make_canonical_path(args[0]), ''
else:
  query_path, query_user = '', ''
results = [e for e in entries if e['canonical_path'].find(query_path) != -1 and ((not query_user) or (e.has_key('U') and e['U'].find(query_user) != -1))]

# print results
HAVE_COLOR_TERM = os.getenv('COLORTERM') or 'color' in os.getenv('TERM', 'default')

def wrap_in_color(text, color_code):
  return '\x1b[%sm%s\x1b[0m' % (color_code, text) if HAVE_COLOR_TERM else text

if len(results) == 0:
  print 'no record found'
  sys.exit(-2)

if len(results) > 1 and not opts.echo and opts.strict:
  print 'multiple records found'
  sys.exit(-3)

for e in results:
  # mark up result
  path = e['canonical_path']
  user = e['U'] if e.has_key('U') else ''
  if query_path:
    path = wrap_in_color(query_path, 33).join(path.split(query_path))
  if query_user:
    user = wrap_in_color(query_user, 33).join(user.split(query_user))
  if user:
    print '%s: %s' % (path,user),
  else:
    print path,

  # display password or copy to clipboard (if only match)
  e['P'] = str(e['P'])
  if opts.echo:
    print '|', wrap_in_color(e['P'], 31),
  elif len(results) == 1:
    xerox.copy(e['P'])
    print '|', wrap_in_color('password copied to clipboard', 32),

  # display url and notes
  if e.has_key('L') and len(results) > 1:
    print '| %s' % e['L'],
  if e.has_key('N') and len(results) > 1:
    print '| ...',
  print

  if e.has_key('L') and len(results) == 1:
    print '  %s' % e['L']
  if e.has_key('N') and len(results) == 1:
    print '  %s' % e['N']
