#! /usr/bin/python
# 
# gksearch.py - Search for the secret from keyring
#
#
# Copyright (c) 2008 Satoru SATOH <satoru.satoh@gmail.com>
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# @see http://library.gnome.org/devel/gnome-keyring/
#

import getpass
import glib
import optparse
import sys

try:
    import gnomekeyring as gk
except:
    print >> sys.stderr, "You need python module for gnome-keyring; "
    print >> sys.stderr, "   e.g. gnome-python2-gnomekeyring in Fedora)."
    raise


def parse_kvpairs(s):
    """Parse given string in format "key0:val0[,key1:val1,...]" and
    construct {key: val, ....}, then returns the result dict.

    >>> parse_kvpairs("key0:val0,key1:val1,key2:val2")
    {'key0': 'val0', 'key1': 'val1', 'key2': 'val2'}
    >>> parse_kvpairs("key0:val0")
    {'key0': 'val0'}
    >>> parse_kvpairs("")
    {}
    >>> parse_kvpairs(":")
    {}
    >>> parse_kvpairs("key0")
    {}
    >>> parse_kvpairs("key0:")
    {}
    >>> parse_kvpairs(":val0")
    {}
    >>> parse_kvpairs("key0:,")
    {}
    """
    ret = {}
    try:
        ret = dict([(k,v) for k,v in [x.split(':') for x in s.split(',')] if k and v])
    except ValueError:
        pass
    return ret


def is_nw_attrs_valid(attrs):
    nw_attrs = {
        'user':None, 'domain':None, 'server':None, 'object':None, 
        'protocol':None, 'authtype':None, 'port':None, 
    }
    return any([(key in attrs.keys()) for key in nw_attrs.keys()])


def find_generic_secrets(attrs, verbose):
    type = gk.ITEM_GENERIC_SECRET
    results = []
    try:
        for k in gk.find_items_sync(type, attrs):
            x = {}
            x['keyring'] = k.keyring
            x['id'] = k.item_id
            x['attributes'] = k.attributes
            x['secret'] = k.secret
            if verbose:
                print >> sys.stderr, "[Info] x = %s" % x
            try:
                info = gk.item_get_info_sync(keyring, id)
                x['display_name'] = info.get_display_name()
            except DeniedError:
                raise  # should not happen.
            results.append(x)
    except gk.NoMatchError:
        pass
    except:
        raise
    return results


def find_network_secrets(attrs, verbose):
    """Search through NetworkSecret with attrs from all keyrings.
    """
    results = []
    try:
        for x in gk.find_network_password_sync(**attrs):
            if verbose:
                print >> sys.stderr, "[Info] x = %s" % x
            # x == {'keyring:..., 'item_id':..., 'secret':..., ...}
            keyring = x.get('keyring')
            id = x.get('item_id')
            x['secret'] = x.get('password')
            x['id'] = id
            try:
                info = gk.item_get_info_sync(keyring, id)
                x['display_name'] = info.get_display_name()
            except DeniedError:
                raise  # should not happen.
            results.append(x)
    except gk.NoMatchError:
        pass
    except:
        raise
    return results


def main():
    prog = sys.argv[0][sys.argv[0].rfind('/')+1:]
    glib.set_application_name(prog)

    # defaults:
    find_method = find_generic_secrets
    verbose = False
    attrs = {}
    format = "#%(id)d: %(display_name)s in keyring %(keyring)s"

    parser = optparse.OptionParser(version='%prog 0.1', usage='%prog [OPTION ...]')
    parser.add_option("-a", "--attrs", dest="attrs", action="store", type="string",
        help="Attrributes for secret in format attr1:val1[,attr2:val2,attr3:val3]")
    parser.add_option("-f", "--format", dest="format", action="store",
        help="Result in given python string style format. \n\t" +
            r'[Default: "#%(id)d: %(display_name)s in keyring %(keyring)s"]')
    parser.add_option("-t", "--type", dest="type", action="store",
        type="string", help="Secret type; generic (default) or network.\n\tNOTE: if network is choosed, you must specify some of attributes;\n\tuser, domain, server, object, protocol, authtype, port.")
    parser.add_option("-v", action="store_true", dest="verbose", help="Verbose mode")
    (options, args) = parser.parse_args()

    if options.verbose:
        verbose = True

    if options.attrs:
        attrs = parse_kvpairs(options.attrs)
        if verbose:
            print >> sys.stderr, "[Info] attrs = %s" % attrs
    else:
        print >> sys.stderr, "Attributes must be specified with --attrs option."
        sys.exit(-1)

    if options.format:
        format = options.format

    if options.type and options.type == 'network':
        find_method = find_network_secrets

    results = find_method(attrs, verbose)

    for res in results:
        print >> sys.stdout, format % res

    exit(0)


if __name__ == '__main__':
    main()

# vim: set sw=4 ts=4 et ai si sm:

