#!/usr/bin/python
import sys
from ldaptor.protocols.ldap import ldapclient, ldif, distinguishedname, ldapconnector, ldapsyntax
from ldaptor.protocols import pureber, pureldap
from ldaptor import usage, ldapfilter
from twisted.internet import protocol, reactor, defer

class Search(ldapclient.LDAPClient):
    def connectionMade(self):
	d=self.bind()
	d.addCallback(self._handle_bind_success)

    def _cbSearch(self, obj):
	a={}
	for attr, vals in obj.items():
	    attr=str(attr)
	    assert not a.has_key(attr)
	    a[attr]=map(str, vals)

	print ':'.join((
	    a['uid'][0],
	    'x',
	    a['uidNumber'][0],
	    a['gidNumber'][0],
	    a.get('gecos', a.get('cn', ['']))[0],
	    a['homeDirectory'][0],
	    a.get('loginShell', [''])[0],
	    ))

    def _handle_bind_success(self, x):
	matchedDN, serverSaslCreds = x
        o = ldapsyntax.LDAPEntry(client=self,
                                 dn=self.factory.base)
        d = o.search(filterObject=self.factory.filt,
                     attributes=self.factory.attributes,
                     callback=self._cbSearch)
        d.chainDeferred(self.factory.deferred)

class SearchFactory(protocol.ClientFactory):
    protocol = Search
    def __init__(self, deferred, base, filt, attributes):
	self.deferred=deferred
	self.base=base
	self.filt=filt
	self.attributes=attributes

    def clientConnectionFailed(self, connector, reason):
	self.deferred.errback(reason)

exitStatus=0

def error(fail):
    print >>sys.stderr, 'fail:', fail.getErrorMessage()
    global exitStatus
    exitStatus=1

def main(base, serviceLocationOverride, filter_text, attributes):
    filt = ldapfilter.parseFilter('(objectClass=posixAccount)')
    if filter_text is not None:
	filt = pureldap.LDAPFilter_and([
	    filt,
	    ldapfilter.parseFilter(filter_text)])
    d=defer.Deferred()
    s=SearchFactory(d, base, filt, attributes)
    d.addErrback(error)
    d.addBoth(lambda x: reactor.stop())

    dn = distinguishedname.DistinguishedName(stringValue=base)
    c=ldapconnector.LDAPConnector(reactor, dn, s, overrides=serviceLocationOverride)
    c.connect()
    reactor.run()
    sys.exit(exitStatus)

class MyOptions(usage.Options, usage.Options_service_location, usage.Options_base):
    """LDAPtor command line search utility"""

    def parseArgs(self, filter=None, *attributes):
	self.opts['filter'] = filter
	self.opts['attributes'] = attributes

if __name__ == "__main__":
    try:
	config = MyOptions()
	config.parseOptions()
    except usage.UsageError, ue:
	sys.stderr.write('%s: %s\n' % (sys.argv[0], ue))
	sys.exit(1)

    main(config.opts['base'],
	 config.opts['service-location'],
	 config.opts['filter'],
	 config.opts['attributes'])
