# Copyright (c) 2000 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

import re
from narval.lib import *
import _ldap

LDAP_NS = NO_NS

MOD_XML = """<?xml version='1.0' encoding='iso-8859-1'?>
<module xmlns:al='%s'>""" % AL_NS


def modify_entry_f(args, doc):
    dn = args['asked-dn'].firstChild.data
    modif_nodes = []
    for n in args['modifs']:
        modif_nodes += n.childNodes
    ldap = args['server']
    host = utf8_to_latin1(ldap.getAttributeNS(LDAP_NS,'host'))
    port = utf8_to_latin1(ldap.getAttributeNS(LDAP_NS,'port'))
    # connect to the LDAP server
    if port == '':
        l = _ldap.open(host)
    else:
        l = _ldap.open(host,int(port))
    l.simple_bind("","")
    modif_list = []
    for modif in modif_nodes:
        attr_value = utf8_to_latin1(modif.getAttributeNS(LDAP_NS,'value'))
        attr_name = utf8_to_latin1(modif.getAttributeNS(LDAP_NS,'name'))
        if modif.nodeName == 'tags':
            continue
        elif modif.nodeName in ['add-attr','add-entry']:
            modif_list.append((_ldap.MOD_ADD,attr_name,attr_value))
        elif modif.nodeName == 'remove-attr':
            modif_list.append((_ldap.MOD_DELETE,attr_name,attr_value))
        elif modif.nodeName == 'replace-attr':
            modif_list.append((_ldap.MOD_REPLACE,attr_name,attr_value))
        else:
            print "Unknown LDAP modification type : ",modif.nodeName
    l.modify_s(dn,modif_list)
    l.unbind_s()
    return {}

MOD_XML = MOD_XML + """
<al:action name='modify_entry' func='modify_entry_f'>
<al:description lang='en'>modifies an entry of the directory</al:description>
<al:input id='server'><al:match>ldap-server[@host]</al:match></al:input>
<al:input id='asked-dn' use='yes'><al:match>asked-dn/text()</al:match></al:input>
<al:input id='modifs' use='yes' list='yes'><al:match>ldap-modifs</al:match></al:input>
</al:action>"""


def consult_directory_f(args, doc):
    asked_dn = args['asked-dn']
    dn = utf8_to_latin1(select_one(asked_dn,'text()').data)
    ldap = args['server']
    host = utf8_to_latin1(ldap.getAttributeNS(LDAP_NS,'host'))
    port = utf8_to_latin1(ldap.getAttributeNS(LDAP_NS,'port'))    
    # connect to the LDAP server
    if port == '':
        l = _ldap.open(host)
    else:
        l = _ldap.open(host,int(port))
    l.simple_bind("","")

    entry_list = doc.createElementNS(LDAP_NS, 'ldap-entries')
    import string
    type = string.upper(utf8_to_latin1(asked_dn.getAttributeNS(LDAP_NS,'scope-type')))
    if type == 'ONELEVEL':
        scope_type = _ldap.SCOPE_ONELEVEL
    elif type == 'BASE':
        scope_type = _ldap.SCOPE_BASE
    elif type in ['SUBTREE','']:
        scope_type = _ldap.SCOPE_SUBTREE
    else:
        print "Warning : unknown scope-type ["+type+"], using default : [SUBTREE]"
        scope_type = _ldap.SCOPE_SUBTREE
    
    
    objectclass = asked_dn.getAttributeNS(LDAP_NS,'objectclass')
    if objectclass == '':
        objectclass = '*'
    entries = l.search_s(dn,scope_type,"objectclass="+objectclass)

    for entry in entries:
        entry_node = doc.createElementNS(LDAP_NS,'entry')
        dn_node = doc.createElementNS(LDAP_NS,'object-dn')
        dn_node.appendChild(doc.createTextNode(entry[0]))
        entry_node.appendChild(dn_node)
        for key,values in entry[1].items():
            key_element = doc.createElementNS(LDAP_NS, key)
            key_element.appendChild(doc.createTextNode(string.join(values,',')))
            entry_node.appendChild(key_element)
        entry_list.appendChild(entry_node)

    l.unbind_s()
    return {'output': entry_list}

MOD_XML = MOD_XML + """
<al:action name='consult_directory' func='consult_directory_f'>
<al:description lang='en'>searches for a dn on the LDAP directory</al:description>
<al:input id='server'><al:match>ldap-server[@host]</al:match></al:input>
<al:input id='asked-dn' use='yes'><al:match>asked-dn/text()</al:match></al:input>
<al:output id='output'><al:match>ldap-entries</al:match></al:output>
</al:action> """


def ldap_url_search_f(args, doc):
    ldap = args['server']
    host = utf8_to_latin1(ldap.getAttributeNS(LDAP_NS,'host'))
    port = utf8_to_latin1(ldap.getAttributeNS(LDAP_NS,'port'))
    # connect to the LDAP server
    if port == '':
        l = _ldap.open(host)
    else:
        l = _ldap.open(host,int(port))
    l.simple_bind("","")
    ldap_url = utf8_to_latin1(args['input_url'].firstChild.data)
    entries = l.url_search_s(ldap_url)
    result_node = doc.createElementNS(LDAP_NS,'ldap-entries')
    for entry in entries:
        entry_node = doc.createElementNS(LDAP_NS,'entry')
        dn_node = doc.createElementNS(LDAP_NS,'object-dn')
        dn_node.appendChild(doc.createTextNode(entry[0]))
        entry_node.appendChild(dn_node)
        for key,values in entry[1].items():
            key_element = doc.createElementNS(LDAP_NS,key)
            key_element.appendChild(doc.createTextNode(string.join(values,',')))
            entry_node.appendChild(key_element)
        result_node.appendChild(entry_node)
    l.unbind_s()
    return {'output':result_node}

MOD_XML = MOD_XML + """
<al:action name='ldap_url_search' func='ldap_url_search_f'>
<al:description lang='en'>
searches in the LDAP directory using LDAP url syntax (see RFC1959)
</al:description>
<al:input id='server'><al:match>ldap-server[@host]</al:match></al:input>
<al:input id='input_url'><al:match>ldap-url</al:match></al:input>
<al:output id='output'><al:match>ldap-entries</al:match></al:output>
</al:action>"""

def add_entry_f(args, doc):
    ldap = args['server']
    encod = get_encoding()
    host = ldap.getAttributeNS(LDAP_NS,'host').encode(encod)
    port = ldap.getAttributeNS(LDAP_NS,'port').encode(encod)
    # connect to the LDAP server
    if not port:
        l = _ldap.open(host)
    else:
        l = _ldap.open(host,int(port))
    l.simple_bind("","")
    # retreive new entry args
    entry = args['ldap-def']
    commonname = select_one(entry, 'common-name/text()').data.encode(encod)
    classe = select_one(entry, 'class/text()').data.encode(encod)
    ou = select_one(entry, 'ou/text()').data.encode(encod)
    dc1 = select_one(entry, 'dc1/text()').data.encode(encod)
    dc2 = select_one(entry, 'dc2/text()').data.encode(encod)
    dn = 'cn=%s, ou=%s, dc=%s, dc=%s'% (commonname, ou, dc1, dc2)
    attr_list = []
    attr_list.append(('objectclass', classe)) 
    attr_list.append(('cn', commonname))    
    for other in select_many(entry,'ldap-attr/*'):
        attr_list.append((other.nodeName.encode(encod),
                          select_one(other,'text()').data.encode(encod)))
    # sends the request to the LDAP server 
    l.add_s(dn, attr_list)
    l.unbind()
    return {}

MOD_XML = MOD_XML + """<al:action name='add_entry' func='add_entry_f'>
 <al:description lang='en'>adds a person in the LDAP directory</al:description>
 <al:input id='server'>
  <al:match>ldap-server[@host]</al:match>
  <al:description lang='en'>LDAP server to request</al:description>
 </al:input>
 <al:input id='ldap-def' use='yes'>
  <al:match>ldap-def[common-name and class and ou and dc1 and dc2]</al:match>
  <al:description lang='en'>
Definition of the entry to add. Required childs: common-name, class, ou, dc1, dc2.
Optionaly it may have childs ldap-attr which has itself a name/text() and value/text() childs.
Those will be add as the entry attributes.
  </al:description>
 </al:input>
</al:action> """

def remove_entry_f(args, doc):
    ldap = args['server']
    host = utf8_to_latin1(ldap.getAttributeNS(LDAP_NS,'host'))
    port = utf8_to_latin1(ldap.getAttributeNS(LDAP_NS,'port'))
    # connect to the LDAP server
    if port == '':
        l = _ldap.open(host)
    else:
        l = _ldap.open(host, int(port))
    l.simple_bind("","")
    dn = args['asked-dn'].firstChild.data
    l.delete_s(dn)
    l.unbind()
    return {}


MOD_XML = MOD_XML + """<al:action name='remove_entry' func='remove_entry_f'>
 <al:description lang='en'>removes an entry from the directory</al:description>
 <al:input id='server'><al:match>ldap-server[@host]</al:match></al:input>
 <al:input id='asked-dn' use='yes'><al:match>asked-dn/text()</al:match></al:input>
</al:action>
"""

MOD_XML = MOD_XML + "</module>"


if __name__ == "__main__" :
    from xml.dom.ext.reader import Sax2
    print MOD_XML
    doc=Sax2.FromXml(MOD_XML)
    print doc.documentElement
