Hi,
I've been working on implementing an RFC 4533 syncrepl consumer using
python-ldap. I can't work out why I can't get the SyncDoneControl that is
returned with the LDAP_RES_SEARCH_RESULT through python-ldap. The
SyncDoneControl contains the cookie that is needed for stateful/minimal
syncrepl refresh only, so losing that control makes the code a lot less
useful.
It looks like SyncStateControl is never returned to the Python code because
Modules/LDAPObject.c l_ldap_result3() calls LDAPmessage_to_python() for
LDAP_RES_SEARCH_ENTRY. LDAPmessage_to_python() discards controls in the
result. I can live with this.
I'm mystified as to why Modules/LDAPObject.c l_ldap_result3() doesn't return
the SyncDoneControl with the LDAP_RES_SEARCH_RESULT. I've waded through the
source and I'm pretty sure that nothing is deliberately filtering out the
control.
Here we can see that the control is in the result when l_ldap_result3()
calls ldap_parse_result():
Breakpoint 2, ldap_parse_result (ld=0x783710, r=0x730830,
errcodep=0x7fff5d8582c4, matcheddnp=0x0, errmsgp=0x0,
referralsp=0x7fff5d8582a8, serverctrls=0x7fff5d8582a0, freeit=0)
at error.c:261
(gdb) p *r->lm_ber
$6 = {ber_opts = {lbo_valid = 2, lbo_options = 1, lbo_debug = 0},
ber_tag = 121, ber_len = 81, ber_usertag = 0,
ber_buf = 0x7e3d60
"\002\001\001yL\200\0301.3.6.1.4.1.4203.1.9.1.4\2010¢.\004,csn=20090317201420Z#000000#00#000000,rid=000",
ber_ptr = 0x7e3d63
"yL\200\0301.3.6.1.4.1.4203.1.9.1.4\2010¢.\004,csn=20090317201420Z#000000#00#000000,rid=000",
ber_end = 0x7e3db1 "", ber_sos = 0x0,
ber_rwptr = 0x0, ber_memctx = 0x0}
(gdb) p *serverctrls
$7 = (LDAPControl **) 0x0
Here is my sample code:
class SyncRequestControl(ldap.controls.LDAPControl):
# The Sync Request Control is an LDAP Control [RFC4511] where the
# controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.1 and the
# controlValue, an OCTET STRING, contains a BER-encoded
# syncRequestValue. The criticality field is either TRUE or FALSE.
# syncRequestValue ::= SEQUENCE {
# mode ENUMERATED {
# -- 0 unused
# refreshOnly (1),
# -- 2 reserved
# refreshAndPersist (3)
# },
# cookie syncCookie OPTIONAL,
# reloadHint BOOLEAN DEFAULT FALSE
# }
# The Sync Request Control is only applicable to the SearchRequest
Message.
controlType='1.3.6.1.4.1.4203.1.9.1.1'
def __init__(self, controlType='1.3.6.1.4.1.4203.1.9.1.1',
criticality=False,
controlValue=None, encodedControlValue=None):
ldap.controls.LDAPControl.__init__(self, self.controlType, criticality,
controlValue, encodedControlValue)
def encodeControlValue(self, value):
# 30 31 Sequence tag (len=0x31)
# 0a 01 01 Enumerated (len=0x01) value 0x01 (RefreshOnly)
# 04 2C Octet String (len=0x2C) value 'csn=...,rid=000'
# 63 73 6e 3d 32 30 30 39 30 33 31 36 31 39 35 35
# 30 39 5a 23 30 30 30 30 30 30 23 30 30 23 30 30
# 30 30 30 30 2c 72 69 64 3d 30 30 30
mode, cookie, reload_hint = value
# Enumerated (len=0x01) value 0x01 (RefreshOnly)
result_content = struct.pack('BBB', 0x0A, 0x01, mode)
# Octet String (optional)
if cookie:
result_content += struct.pack('BB', 0x04, len(cookie))
result_content += cookie
# Boolean (optional)
if reload_hint:
result_content += struct.pack('BBB', 0x01, 0x01, 0xFF)
# Sequence tag
result_header = struct.pack('BB', 0x30, len(result_content))
return result_header + result_content
ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3)
ldap.set_option(ldap.OPT_REFERRALS, 0)
conn = ldap.initialize(host)
sync_req_ctrl=SyncRequestControl(criticality=True,
controlValue=(3, cookie, True))
op_id = conn.search_ext(base, ldap.SCOPE_ONELEVEL,
attrlist=('dn',),
serverctrls=(sync_req_ctrl,),
)
# ldap.controls.knownLDAPControls[SyncStateControl.controlType] =
SyncStateControl
# ldap.controls.knownLDAPControls[SyncDoneControl.controlType] =
SyncDoneControl
#rtype, rdata, rmsgid, decoded_serverctrl = conn.result3(all=0, timeout=60)
rtype, rdata, rmsgid, serverctrls =
conn._ldap_call(conn._l.result3,_ldap.RES_ANY,0,60)
while rtype:
print 'rtype=%s' % rtype
print 'rdata=%s' % rdata
print 'rmsgid=%s' % rmsgid
print 'serverctrls=%s' % serverctrls
print ''
# rtype, rdata, rmsgid, decoded_serverctrl = conn.result3(all=0,
timeout=10)
rtype, rdata, rmsgid, serverctrls =
conn._ldap_call(conn._l.result3,_ldap.RES_ANY,0,10)
--
Thanks,
Sean Burford
------------------------------------------------------------------------------
Apps built with the Adobe(R) Flex(R) framework and Flex Builder(TM) are
powering Web 2.0 with engaging, cross-platform capabilities. Quickly and
easily build your RIAs with Flex Builder, the Eclipse(TM)based development
software that enables intelligent coding and step-through debugging.
Download the free 60 day trial. http://p.sf.net/sfu/www-adobe-com
_______________________________________________
Python-LDAP-dev mailing list
Python-LDAP-dev@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/python-ldap-dev