Skip to content

Commit 8492b16

Browse files
committed
WIP on #278
Would be nice if result4 returned the operation result as well (but for that we'd need to expose the result codes too, at the moment all of them are just exceptions that can't even be converted to ints).
1 parent 4820ebe commit 8492b16

File tree

11 files changed

+235
-91
lines changed

11 files changed

+235
-91
lines changed

Doc/fake_ldap_module_for_documentation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ def get_option(num):
2828

2929
class LDAPError:
3030
pass
31+
32+
RAISE_ALL = 2

Doc/reference/ldap.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -939,10 +939,10 @@ and wait for and return with the server's result, or with
939939
This method behaves almost exactly like :py:meth:`result2()`. But it
940940
returns an extra item in the tuple, the decoded server controls.
941941

942-
.. py:method:: LDAPObject.result4([msgid=RES_ANY [, all=1 [, timeout=None [, add_ctrls=0 [, add_intermediates=0 [, add_extop=0 [, resp_ctrl_classes=None]]]]]]]) -> 6-tuple
942+
.. py:method:: LDAPObject.result4([msgid=RES_ANY [, all=1 [, timeout=None [, add_ctrls=0 [, add_intermediates=0 [, add_extop=0 [, resp_ctrl_classes=None]]]]]]]) -> 7-tuple
943943
944944
This method behaves almost exactly like :py:meth:`result3()`. But it
945-
returns an extra items in the tuple, the decoded results of an extended response.
945+
returns extra items in the tuple, the result code and decoded results of an extended response.
946946

947947
The additional arguments are:
948948

Lib/ldap/ldapobject.py

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from ldap.extop import ExtendedRequest,ExtendedResponse
3131
from ldap.compat import reraise
3232

33-
from ldap import LDAPError
33+
from ldap import LDAPError, RAISE_ALL
3434

3535
PY2 = sys.version_info[0] <= 2
3636
if PY2:
@@ -96,14 +96,14 @@ class SimpleLDAPObject:
9696
def __init__(
9797
self,uri,
9898
trace_level=0,trace_file=None,trace_stack_limit=5,bytes_mode=None,
99-
bytes_strictness=None,
99+
bytes_strictness=None,raise_for_result=RAISE_ALL,
100100
):
101101
self._trace_level = trace_level or ldap._trace_level
102102
self._trace_file = trace_file or ldap._trace_file
103103
self._trace_stack_limit = trace_stack_limit
104104
self._uri = uri
105105
self._ldap_object_lock = self._ldap_lock('opcall')
106-
self._l = ldap.functions._ldap_function_call(ldap._ldap_module_lock,_ldap.initialize,uri)
106+
self._l = ldap.functions._ldap_function_call(ldap._ldap_module_lock,_ldap.initialize,uri,raise_for_result)
107107
self.timeout = -1
108108
self.protocol_version = ldap.VERSION3
109109

@@ -520,14 +520,16 @@ def compare_ext(self,dn,attr,value,serverctrls=None,clientctrls=None):
520520
def compare_ext_s(self,dn,attr,value,serverctrls=None,clientctrls=None):
521521
msgid = self.compare_ext(dn,attr,value,serverctrls,clientctrls)
522522
try:
523-
ldap_res = self.result3(msgid,all=1,timeout=self.timeout)
523+
ldap_res = self.result4(msgid,all=1,timeout=self.timeout)
524+
if ldap_res[1] == ldap.COMPARE_TRUE.errnum:
525+
return True
526+
if ldap_res[1] == ldap.COMPARE_FALSE.errnum:
527+
return False
528+
return ldap_res
524529
except ldap.COMPARE_TRUE:
525530
return True
526531
except ldap.COMPARE_FALSE:
527532
return False
528-
raise ldap.PROTOCOL_ERROR(
529-
'Compare operation returned wrong result: %r' % (ldap_res,)
530-
)
531533

532534
def compare(self,dn,attr,value):
533535
return self.compare_ext(dn,attr,value,None,None)
@@ -576,7 +578,7 @@ def extop(self,extreq,serverctrls=None,clientctrls=None):
576578
return self._ldap_call(self._l.extop,extreq.requestName,extreq.encodedRequestValue(),RequestControlTuples(serverctrls),RequestControlTuples(clientctrls))
577579

578580
def extop_result(self,msgid=ldap.RES_ANY,all=1,timeout=None):
579-
resulttype,msg,msgid,respctrls,respoid,respvalue = self.result4(msgid,all=1,timeout=self.timeout,add_ctrls=1,add_intermediates=1,add_extop=1)
581+
msgtype,result,msg,msgid,respctrls,respoid,respvalue = self.result4(msgid,all=1,timeout=self.timeout,add_ctrls=1,add_intermediates=1,add_extop=1)
580582
return (respoid,respvalue)
581583

582584
def extop_s(self,extreq,serverctrls=None,clientctrls=None,extop_resp_class=None):
@@ -737,39 +739,39 @@ def result(self,msgid=ldap.RES_ANY,all=1,timeout=None):
737739
If a timeout occurs, a TIMEOUT exception is raised, unless
738740
polling (timeout = 0), in which case (None, None) is returned.
739741
"""
740-
resp_type, resp_data, resp_msgid = self.result2(msgid,all,timeout)
741-
return resp_type, resp_data
742+
msgtype, resp_data, resp_msgid = self.result2(msgid,all,timeout)
743+
return msgtype, resp_data
742744

743745
def result2(self,msgid=ldap.RES_ANY,all=1,timeout=None):
744-
resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all,timeout)
745-
return resp_type, resp_data, resp_msgid
746+
msgtype, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all,timeout)
747+
return msgtype, resp_data, resp_msgid
746748

747749
def result3(self,msgid=ldap.RES_ANY,all=1,timeout=None,resp_ctrl_classes=None):
748-
resp_type, resp_data, resp_msgid, decoded_resp_ctrls, retoid, retval = self.result4(
750+
msgtype, result, resp_data, resp_msgid, decoded_resp_ctrls, retoid, retval = self.result4(
749751
msgid,all,timeout,
750752
add_ctrls=0,add_intermediates=0,add_extop=0,
751753
resp_ctrl_classes=resp_ctrl_classes
752754
)
753-
return resp_type, resp_data, resp_msgid, decoded_resp_ctrls
755+
return msgtype, resp_data, resp_msgid, decoded_resp_ctrls
754756

755757
def result4(self,msgid=ldap.RES_ANY,all=1,timeout=None,add_ctrls=0,add_intermediates=0,add_extop=0,resp_ctrl_classes=None):
756758
if timeout is None:
757759
timeout = self.timeout
758760
ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates,add_extop)
759761
if ldap_result is None:
760-
resp_type, resp_data, resp_msgid, resp_ctrls, resp_name, resp_value = (None,None,None,None,None,None)
762+
resp_type, result, resp_data, resp_msgid, resp_ctrls, resp_name, resp_value = (None,None,None,None,None,None,None)
761763
else:
762-
if len(ldap_result)==4:
763-
resp_type, resp_data, resp_msgid, resp_ctrls = ldap_result
764+
if len(ldap_result)==5:
765+
resp_type, result, resp_data, resp_msgid, resp_ctrls = ldap_result
764766
resp_name, resp_value = None,None
765767
else:
766-
resp_type, resp_data, resp_msgid, resp_ctrls, resp_name, resp_value = ldap_result
768+
resp_type, result, resp_data, resp_msgid, resp_ctrls, resp_name, resp_value = ldap_result
767769
if add_ctrls:
768770
resp_data = [ (t,r,DecodeControlTuples(c,resp_ctrl_classes)) for t,r,c in resp_data ]
769771
decoded_resp_ctrls = DecodeControlTuples(resp_ctrls,resp_ctrl_classes)
770772
if resp_data is not None:
771773
resp_data = self._bytesify_results(resp_data, with_ctrls=add_ctrls)
772-
return resp_type, resp_data, resp_msgid, decoded_resp_ctrls, resp_name, resp_value
774+
return resp_type, result, resp_data, resp_msgid, decoded_resp_ctrls, resp_name, resp_value
773775

774776
def search_ext(self,base,scope,filterstr=None,attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
775777
"""
@@ -1086,7 +1088,7 @@ class ReconnectLDAPObject(SimpleLDAPObject):
10861088
def __init__(
10871089
self,uri,
10881090
trace_level=0,trace_file=None,trace_stack_limit=5,bytes_mode=None,
1089-
bytes_strictness=None, retry_max=1, retry_delay=60.0
1091+
bytes_strictness=None, raise_for_result=RAISE_ALL, retry_max=1, retry_delay=60.0
10901092
):
10911093
"""
10921094
Parameters like SimpleLDAPObject.__init__() with these
@@ -1102,7 +1104,8 @@ def __init__(
11021104
self._last_bind = None
11031105
SimpleLDAPObject.__init__(self, uri, trace_level, trace_file,
11041106
trace_stack_limit, bytes_mode,
1105-
bytes_strictness=bytes_strictness)
1107+
bytes_strictness=bytes_strictness,
1108+
raise_for_result=raise_for_result)
11061109
self._reconnect_lock = ldap.LDAPLock(desc='reconnect lock within %s' % (repr(self)))
11071110
self._retry_max = retry_max
11081111
self._retry_delay = retry_delay

Lib/ldap/syncrepl.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,15 +399,15 @@ def syncrepl_poll(self, msgid=-1, timeout=None, all=0):
399399
400400
"""
401401
while True:
402-
type, msg, mid, ctrls, n, v = self.result4(
402+
msgtype, result, msg, mid, ctrls, n, v = self.result4(
403403
msgid=msgid,
404404
timeout=timeout,
405405
add_intermediates=1,
406406
add_ctrls=1,
407407
all=0,
408408
)
409409

410-
if type == 101:
410+
if msgtype == 101:
411411
# search result. This marks the end of a refreshOnly session.
412412
# look for a SyncDone control, save the cookie, and if necessary
413413
# delete non-present entries.
@@ -420,7 +420,7 @@ def syncrepl_poll(self, msgid=-1, timeout=None, all=0):
420420

421421
return False
422422

423-
elif type == 100:
423+
elif msgtype == 100:
424424
# search entry with associated SyncState control
425425
for m in msg:
426426
dn, attrs, ctrls = m
@@ -439,7 +439,7 @@ def syncrepl_poll(self, msgid=-1, timeout=None, all=0):
439439
self.syncrepl_set_cookie(c.cookie)
440440
break
441441

442-
elif type == 121:
442+
elif msgtype == 121:
443443
# Intermediate message. If it is a SyncInfoMessage, parse it
444444
for m in msg:
445445
rname, resp, ctrls = m

Modules/LDAPObject.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ static void free_attrs(char ***);
2121
/* constructor */
2222

2323
LDAPObject *
24-
newLDAPObject(LDAP *l)
24+
newLDAPObject(LDAP *l, int raise_for_result)
2525
{
2626
LDAPObject *self = (LDAPObject *)PyObject_NEW(LDAPObject, &LDAP_Type);
2727

@@ -30,6 +30,7 @@ newLDAPObject(LDAP *l)
3030
self->ldap = l;
3131
self->_save = NULL;
3232
self->valid = 1;
33+
self->raise_for_result = raise_for_result;
3334
return self;
3435
}
3536

@@ -1086,6 +1087,7 @@ l_ldap_result4(LDAPObject *self, PyObject *args)
10861087
char *retoid = 0;
10871088
PyObject *valuestr = NULL;
10881089
int result = LDAP_SUCCESS;
1090+
int should_raise = 0;
10891091
LDAPControl **serverctrls = 0;
10901092

10911093
if (!PyArg_ParseTuple
@@ -1161,7 +1163,21 @@ l_ldap_result4(LDAPObject *self, PyObject *args)
11611163
LDAP_END_ALLOW_THREADS(self);
11621164
}
11631165

1164-
if (result != LDAP_SUCCESS) { /* result error */
1166+
switch (result) {
1167+
case LDAP_SUCCESS:
1168+
should_raise = 0;
1169+
break;
1170+
case LDAP_COMPARE_FALSE:
1171+
case LDAP_COMPARE_TRUE:
1172+
case LDAP_REFERRAL:
1173+
case LDAP_SASL_BIND_IN_PROGRESS:
1174+
if ( self->raise_for_result <= RAISE_ON_ERROR ) break;
1175+
/* Fallthrough */
1176+
default:
1177+
should_raise = self->raise_for_result > DONT_RAISE;
1178+
break;
1179+
}
1180+
if (should_raise) {
11651181
ldap_controls_free(serverctrls);
11661182
Py_XDECREF(valuestr);
11671183
return LDAPraise_for_message(self->ldap, "ldap_parse_result", msg);
@@ -1189,13 +1205,14 @@ l_ldap_result4(LDAPObject *self, PyObject *args)
11891205
else {
11901206
/* s handles NULL, but O does not */
11911207
if (add_extop) {
1192-
retval = Py_BuildValue("(iOiOsO)", res_type, pmsg, res_msgid,
1193-
pyctrls, retoid,
1208+
retval = Py_BuildValue("(iiOiOsO)", res_type, result, pmsg,
1209+
res_msgid, pyctrls, retoid,
11941210
valuestr ? valuestr : Py_None);
11951211
}
11961212
else {
11971213
retval =
1198-
Py_BuildValue("(iOiO)", res_type, pmsg, res_msgid, pyctrls);
1214+
Py_BuildValue("(iiOiO)", res_type, result, pmsg, res_msgid,
1215+
pyctrls);
11991216
}
12001217

12011218
if (pmsg != Py_None) {

Modules/LDAPObject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ typedef struct {
2121
PyObject_HEAD LDAP *ldap;
2222
_threadstate _save; /* for thread saving on referrals */
2323
int valid;
24+
int raise_for_result;
2425
} LDAPObject;
2526

2627
extern PyTypeObject LDAP_Type;
2728

2829
#define LDAPObject_Check(v) (Py_TYPE(v) == &LDAP_Type)
2930

30-
extern LDAPObject *newLDAPObject(LDAP *);
31+
extern LDAPObject *newLDAPObject(LDAP *, int);
3132

3233
/* macros to allow thread saving in the context of an LDAP connection */
3334

Modules/constants.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,13 @@ LDAPinit_constants(PyObject *m)
205205
if (PyModule_AddIntConstant(m, "OPT_OFF", 0) != 0)
206206
return -1;
207207

208+
if (PyModule_AddIntMacro(m, DONT_RAISE) != 0)
209+
return -1;
210+
if (PyModule_AddIntMacro(m, RAISE_ON_ERROR) != 0)
211+
return -1;
212+
if (PyModule_AddIntMacro(m, RAISE_ALL) != 0)
213+
return -1;
214+
208215
/* exceptions */
209216

210217
LDAPexception_class = PyErr_NewException("ldap.LDAPError", NULL, NULL);

Modules/constants.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,8 @@ PyObject *LDAPerr(int errnum);
2323
#define LDAP_CONTROL_VALUESRETURNFILTER "1.2.826.0.1.3344810.2.3" /* RFC 3876 */
2424
#endif /* !LDAP_CONTROL_VALUESRETURNFILTER */
2525

26+
#define DONT_RAISE 0
27+
#define RAISE_ON_ERROR 1
28+
#define RAISE_ALL 2
29+
2630
#endif /* __h_constants_ */

Modules/functions.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ static PyObject *
1313
l_ldap_initialize(PyObject *unused, PyObject *args)
1414
{
1515
char *uri;
16+
int raise_for_result = RAISE_ALL;
1617
LDAP *ld = NULL;
1718
int ret;
1819
PyThreadState *save;
1920

20-
if (!PyArg_ParseTuple(args, "s:initialize", &uri))
21+
if (!PyArg_ParseTuple(args, "s|i:initialize", &uri, &raise_for_result))
2122
return NULL;
2223

2324
save = PyEval_SaveThread();
@@ -27,7 +28,7 @@ l_ldap_initialize(PyObject *unused, PyObject *args)
2728
if (ret != LDAP_SUCCESS)
2829
return LDAPerror(ld, "ldap_initialize");
2930

30-
return (PyObject *)newLDAPObject(ld);
31+
return (PyObject *)newLDAPObject(ld, raise_for_result);
3132
}
3233

3334
/* ldap_str2dn */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy