#!/usr/bin/env python3

import sys
from ldaptor.protocols.ldap import ldapclient, ldapsyntax
from ldaptor.protocols import pureldap
from twisted.internet.defer import Deferred, DeferredList
from twisted.internet import protocol, reactor


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

    def _printResults(self, result, host):
        for context in result["namingContexts"]:
            print("{}\t{}".format(host, context))

    def _handle_bind_success(self, x):
        matchedDN, serverSaslCreds = x
        o = ldapsyntax.LDAPEntry(client=self, dn="")
        d = o.search(
            filterText="(objectClass=*)",
            scope=pureldap.LDAP_SCOPE_baseObject,
            attributes=["namingContexts"],
            callback=(lambda x: self._printResults(x, self.factory.server)),
        )
        d.chainDeferred(self.factory.deferred)


class SearchFactory(protocol.ClientFactory):
    protocol = Search

    def __init__(self, server, deferred):
        self.server = server
        self.deferred = deferred

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


exitStatus = 0


def errback(data):
    print("ERROR:", data.getErrorMessage())
    global exitStatus
    exitStatus = 1


def main(servers):
    l = []
    for server in servers:
        d = Deferred()
        l.append(d)
        s = SearchFactory(server, d)
        reactor.connectTCP(server, 389, s)
        d.addErrback(errback)
    dl = DeferredList(l)
    dl.addBoth(lambda x: reactor.stop())
    reactor.run()
    sys.exit(exitStatus)


if __name__ == "__main__":
    if not sys.argv[1:]:
        print("%s: usage:" % sys.argv[0], file=sys.stderr)
        print("  %s HOST.." % sys.argv[0], file=sys.stderr)
    else:
        main(sys.argv[1:])
