Hi All,
I'm hoping someone can give me a hand to link to the C LDAP API. The aim is to access LDAP data from GNU Smalltalk server scripts and I figure using the C interface is the simplest way of doing this. I haven't done much C programming. Secondly, I'm running Mac OS X 10.5, and have Linux available as well if need be. LDAP C API As far as I can see there are no callbacks in the LDAP API, so Library calls should be fine. Section 5.2 of the documentation says "First, the mapping between these functions and string names for the functions needs to be established in your module". So in order to call the LDAP C API from Smalltalk I have some questions:- 1. How does one action "DLD class>>#addLibrary:" so as to link GNU Smalltalk with the LDAP library? I don't know what it means! 2. Where is the mapping specified between the C function, and the Smalltalk name that I am to provide for the function? I wasn't able to find this in the documentation. For example, the first function is an ldap_init and the C definition looks like this:- #include <ldap.h> LDAP *ldap_open(host, port) char *host; int port; LDAP *ldap_init(host, port) char *host; int port; What are the steps required to map the function above to a definition similar to the one below? ldap_open: aString <cCall: 'system' returning: #cObject args: ... (ldap api is at http://www.openldap.org/software/man.cgi?query=ldap_open&apropos=0&sektion=3&manpath=OpenLDAP+2.0-Release&format=html ) 3. In the definition above, I'm expecting to get back a pointer to an LDAP struct. Would the return type in the Smalltalk function be a "cObject"? If it helps, the definition in ldap.h for the LDAP struct is just... typedef struct ldap LDAP; My aim is to get the program below running from Smalltalk if at all possible! ------------------ http://docs.sun.com/source/816-5616-10/example.htm#16961 #include <stdio.h> #include "ldap.h" /* Adjust these setting for your own LDAP server */ #define HOSTNAME "localhost" #define PORT_NUMBER LDAP_PORT #define FIND_DN "uid=bjensen, ou=People, o=airius.com" int main( int argc, char **argv ) { LDAP *ld; LDAPMessage *result, *e; BerElement *ber; char *a; char **vals; int i, rc; /* Get a handle to an LDAP connection. */ if ( (ld = ldap_init( HOSTNAME, PORT_NUMBER )) == NULL ) { perror( "ldap_init" ); return( 1 ); } /* Bind anonymously to the LDAP server. */ rc = ldap_simple_bind_s( ld, NULL, NULL ); if ( rc != LDAP_SUCCESS ) { fprintf(stderr, "ldap_simple_bind_s: %s\n", ldap_err2string(rc)); return( 1 ); } /* Search for the entry. */ if ( ( rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE, "(objectclass=*)", NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &result ) ) != LDAP_SUCCESS ) { fprintf(stderr, "ldap_search_ext_s: %s\n", ldap_err2string(rc)); return( 1 ); } /* Since we are doing a base search, there should be only one matching entry. */ e = ldap_first_entry( ld, result ); if ( e != NULL ) { printf( "\nFound %s:\n\n", FIND_DN ); /* Iterate through each attribute in the entry. */ for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL; a = ldap_next_attribute( ld, e, ber ) ) { /* For each attribute, print the attribute name and values. */ if ((vals = ldap_get_values( ld, e, a)) != NULL ) { for ( i = 0; vals[i] != NULL; i++ ) { printf( "%s: %s\n", a, vals[i] ); } ldap_value_free( vals ); } ldap_memfree( a ); } if ( ber != NULL ) { ber_free( ber, 0 ); } } ldap_msgfree( result ); ldap_unbind( ld ); return( 0 ); } ------------------------- Thank you Stephen _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
> So in order to call the LDAP C API from Smalltalk I have some questions:- > 1. How does one action "DLD class>>#addLibrary:" so as to link GNU > Smalltalk with the LDAP library? I don't know what it means! You just have to add something like DLD addLibrary: 'libldap' before the method definitions. > 2. Where is the mapping specified between the C function, and the > Smalltalk name that I am to provide for the function? I wasn't able to > find this in the documentation. For example, the first function is an > ldap_init and the C definition looks like this:- There are two main possibilities: everything goes on the class side, or constructor functions go on the class side and everything else on the instance side. In this case, you'd probably opt for the latter. Also, do you want your methods named #open:/#init: or #ldapOpen:/#ldapInit:? Or maybe more Smalltalk-ish #openOn:/#on:? These are the "design" choices to be made. > 3. In the definition above, I'm expecting to get back a pointer to an > LDAP struct. Would the return type in the Smalltalk function be a > "cObject"? No, it would be #{LDAP} so that you can use APIs like ldap_simple_bind_s as instance methods of LDAP. For arguments, instead, just use #cObject. > If it helps, the definition in ldap.h for the LDAP struct is just... > typedef struct ldap LDAP; If it is opaque, just define CObject subclass: LDAP [] otherwise you would have to use CStruct (there are examples in the Cairo bindings. Here are the definitions for a couple of methods (untested) DLD addLibrary: 'libldap'. CObject subclass: LDAP [ LDAP class >> openOn: host port: port [ "Maybe you prefer #open:port: as the selector, of course." <cCall: 'ldap_open' returning: #{LDAP} args: #(#string #int)> ] LDAP class >> on: host port: port [ "Maybe you prefer #init:port: as the selector, of course." <cCall: 'ldap_init' returning: #{LDAP} args: #(#string #int)> ] LDAP class >> errorString [ "Utility methods might also go on the class side." <cCall: 'ldap_err2string' returning: #string args: #(#int)> ] simpleBindS: foo bar: bar [ "Didn't bother looking at the documentation, so I don't know what foo and bar actually are... I declared them as #cObject below, but maybe #string is better... In any case, passing `nil' gives a NULL to the C side." <cCall: 'ldap_simple_bind_s' returning: #int args: #(#self #cObject #cObject)> ] ] Other random things to know: - to pass a pointer by reference, use #cObjectPtr and pass a variable that was set to "CObject new". - to return an array of strings use #{CString} as the return type (for ldap_get_values). Then, you can do "(array at: 0)", "(array at: 1)", etc. to get the strings, or use the other idiom in the translated program below. You can also make a subclass of CString and return that class, so that you can override #free to call ldap_value_free. I assume this below. - Likewise, you can make a subclass of CObject for the value returned by ldap_first_attribute and ldap_next_attribute, so that you can override #free to call ldap_memfree. Similarly, you can make a subclass of CObject for BER (ber_free) and LDAPMessage (ldap_msgfree). Do that just to override #free, or as the beginning of a `more Smalltalk-looking' implementation (more hints below). - You also need to define constant methods, probably on the class side. That's not hard to do with sed starting from #defines. Enums are a little harder. Here is the translation of your program to Smalltalk, with more commentary: CObject extend [ "Will be in 3.1" isNull [ ^address = 0 ] ] hostName := 'localhost'. portNumber := LDAP defaultPort. "LDAP_PORT" findDN := 'uid=bjensen, ou=People, o=airius.com'. ldap := LDAP on: hostName port: portNumber. ldap isNil ifTrue: [ File checkError. ObjectMemory quit: 1 ]. "Omitting more error checking. You can also wrap the functions in `two levels': define the C calls with methods like #primSimpleBindS:bar:, and wrappers that check the return code and raise an exception if the return code is bad. As an advantage, you can also create the CObjects for #cObjectPtr parameters in the wrapper and return them. On an even higher level, you could add a field in LDAPMessage to point back to the LDAP object, so as to have methods like LDAPMessage>>first ^self ldap firstEntry: self or even LDAPMessage>>keysAndValuesDo: implementing the loop below, to be used like e keysAndValuesDo: [ :a :vals | vals do: [ :each | (a->each) printNl ] ] As you can see, designing basic bindings is one thing and can be automated (e.g. if SWIG had GNU Smalltalk support); designing bindings that look native is another thing and very hard to automate. For an example, check out packages/gdbm which has the same example using the low-level API and a high-level Dictionary-like binding." ldap simpleBindS: nil bar: nil. ldap searchExtS: findDN scope: LDAP scopeBase ???: 'objectclass=*' ???: nil ???: 0 ???: nil ???: nil ???: LDAP noLimit ???: LDAP noLimit result: (result := LDAPMessage new). e := ldap firstEntry: result. e isNil ifFalse: [ ('Found ', findDN) displayNl. "Iterate through each attribute in the entry. Should return a #string" aPtr := ldap firstAttribute: e ber: (ber := BER new). [ aPtr isNil ] whileFalse: [ "Convert as soon as possible to a String, and use that in the remainder. In more complicated examples it would cause less memory management headaches." a := String fromCData: aPtr. aPtr free. vals := ldap getValues: e attribute: a. vals isNull ifTrue: [ val := vals. [ val value isNil ] whileFalse: [ (a->val value) printNl. val incr ]. vals free ] a := ldap nextAttribute: e ber: ber. ]. ]. "I don't know if it makes sense but BER>>#free could call `self free: 0'." ber isNil ifFalse: [ ber free ]. result free. ldap unbind That's it, HTH! Paolo _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
> vals := ldap getValues: e attribute: a. > vals isNull ifTrue: [ Thinking more about it, I'm not sure here, maybe #isNil is needed instead of #isNull. Paolo _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
In reply to this post by Paolo Bonzini-2
Hi,
Paolo, I've been working through the program outline you replied with and have an authenticated connection to the LDAP server working. I'm now trying to call ldap_search to get some LDAP entries returned, but have an error when I call the search API. I've included the script code below at the end of this message... if the email client wraps and makes it difficult to use, I can make it available on our web server. The error I'm getting is this:- stephenw$ ./ldapapi.st started ... about to run the search. ./ldapapi.st:117: Attempt to pass an instance of SmallInteger as a void * (ip 14)CFunctionDescriptor(CCallable)>>#callInto: (ip 8)LDAP>>#searchWithBase:scope:filter:attributes:isAttrsOnly:serverctrls:clientctrls:timeout:sizelimit:searchResult: (ip 62)UndefinedObject>>#executeStatements (ip 0)<bottom> Abort trap So what is causing the error is the question! Looking at the ldap_search_ext_s definition, all the parameters are quite straightforward except (I think) "scope" and "LDAPMessage". First of all, re scope, the sample program provided in my first post inserts LDAP_SCOPE_BASE for this parameter and when I looked that up in ldap.h I get #define LDAP_SCOPE_BASE ((ber_int_t) 0x0000) I don't know what ber_int_t does, so tried both of 0 and 16r0000 in my program. No change. The second thing that it would be good to check is the definition in Smalltalk for ldap_search_ext_s, and in particular how I've defined #{LDAPMessage}. Is it correct? Thank you for your assistance - I trust the fix is something simple. --- By the way: how is one supposed to define class variables? I used this link http://www.gnu.org/software/smalltalk/manual/gst.html#Syntax and defined scopeBase and nolimit in the LDAP class. I tried inserting LDAP nolimit as you did in your sample code (timeout: LDAP nolimit),but got an error.. LDAP error: did not understand #nolimit MessageNotUnderstood(Exception)>>signal (AnsiExcept.st:216) Cheers Regards Stephen ---------------------------------- stephenw$ cat ldapapi.st.post #!/usr/local/bin/gst -f "call LDAP API" " CObject extend [ " "Method commented out for now; wouldn't compile" " isNull [ ^address = 0 ] ] " CObject subclass: LDAPMessage [ LDAPMessage class >> free [ <cCall: 'ldap_msgfree' returning: #int args: #(#self)> ] ] CObject subclass: LDAP [ "class variables" scopeBase := 0. nolimit := 0. LDAP class >> openOn: host port: port [ <cCall: 'ldap_open' returning: #{LDAP} args: #(#string #int)> ] LDAP class >> errorString: resultNum [ "Utility methods might also go on the class side." <cCall: 'ldap_err2string' returning: #string args: #(#int)> ] simpleBindWithDN: who passwd: secret [ <cCall: 'ldap_simple_bind_s' returning: #int args: #(#self #string #string)> ] " ldap_search_ext_s C definition -------------- LDAP_F( int ) ldap_search_ext_s LDAP_P(( LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter, char **attrs, int attrsonly, LDAPControl **serverctrls, LDAPControl **clientctrls, struct timeval *timeout, int sizelimit, LDAPMessage **res )); rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE, ""(objectclass=*)"", NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &result --------------- " searchWithBase: baseDN scope: aScope filter: aFilter attributes: theAttribs isAttrsOnly: attrsOnly serverctrls: serverControl clientctrls: clientControl timeout: timeval sizelimit: numResults searchResult: aSearchResult [ <cCall: 'ldap_search_ext_s' returning: #int args: #(#self #string #int #string #cObject #int #cObject #cObject #cObject #int #{LDAPMessage})> ] " ldap_first_entry C definition -------------- LDAP_F( LDAPMessage * ) ldap_first_entry LDAP_P(( LDAP *ld, LDAPMessage *chain )); --------------" firstEntry: searchResult [ <cCall: 'ldap_first_entry' returning: #{LDAPMessage} args: #(#self #{LDAPMessage})> ] ] "mainline" | ldapcall ldap resultCode errorMsg hostName baseDN | Transcript showCr: 'started'. DLD addLibrary: 'libldap'. hostName := 'aserver'. baseDN := 'ou=Users,dc=example,dc=com'. supervisorDN := 'uid=sw,ou=Users,dc=example,dc=com'. ldap := LDAP openOn: hostName port: 389. ldap isNil ifTrue: [ File checkError. ObjectMemory quit: 1 ]. resultCode := ldap simpleBindWithDN: supervisorDN passwd: 'apwd'. (resultCode > 0) ifTrue: [ "Transcript showCr: ('%1 is result from bind' % { resultCode })." errorCodeMsg := LDAP errorString: resultCode. Transcript showCr: 'The LDAP Bind failed with the message: ',errorCodeMsg ]. (resultCode = 0) ifTrue: [ Transcript showCr: '... about to run the search.'. resultCode := ldap searchWithBase: supervisorDN scope: 16r0000 filter: '(objectclass=*)' attributes: nil isAttrsOnly: 0 serverctrls: nil clientctrls: nil timeout: 0 sizelimit: 0 searchResult: (searchResult := LDAPMessage new). Transcript showCr: '... search completed'. ] " entry := ldap firstEntry: searchResult. entry isNil ifFalse: [ ('Found results with base DN: ', baseDN) displayN1. ] " ! _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
> stephenw$ ./ldapapi.st
> started > ... about to run the search. > ./ldapapi.st:117: Attempt to pass an instance of SmallInteger as a void * timeout: 0 should be "timeout: nil". I should add an argument index to the error message. Also, here: > <cCall: 'ldap_search_ext_s' returning: #int args: #(#self > #string #int #string #cObject #int #cObject #cObject #cObject #int > #{LDAPMessage})> ... the final argument should be #cObjectPtr (void **). > > " ldap_first_entry C definition -------------- > LDAP_F( LDAPMessage * ) > ldap_first_entry LDAP_P(( > LDAP *ld, > LDAPMessage *chain )); > --------------" > firstEntry: searchResult [ > <cCall: 'ldap_first_entry' returning: #{LDAPMessage} args: > #(#self #{LDAPMessage})> ] Likewise, here it can be #cObject, i.e. void *, but it should not be a problem to leave it as is. Paolo _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
In reply to this post by Stephen-71
Hi,
Thanks Paolo. Lots of progress, I have a query mostly running now! Here is the issue... I've run the script over a number of LDAP entries and it is consistently tripping up at the mail attribute. It isn't as though the mail attribute is always in the nth index of an entry either; at whatever index the mail attribute occurs in the attribute list, the script will crash at that point. Here is the output of the script attached below:- started ... about to run the search. ... search completed Found results with base DN: ou=Users,dc=example,dc=com Found first attribute 'givenName' 'givenName'->'Test --' 'sn' 'sn'->'email' 'loginShell' 'loginShell'->'/bin/bash' 'uidNumber' 'uidNumber'->'2008' 'gidNumber' 'gidNumber'->'2008' 'displayName' 'displayName'->'Testing email' 'mail' Object: nil error: did not understand #value MessageNotUnderstood(Exception)>>signal (AnsiExcept.st:216) UndefinedObject(Object)>>doesNotUnderstand: #value (AnsiExcept.st:1484) UndefinedObject>>executeStatements (ldapapi.st:171) For comparison, here is the output from an ldapsearch query on the same record:- # sw-apple, Users, example.com dn: uid=sw-apple,ou=Users,dc=example,dc=com givenName: Test -- sn: email loginShell: /bin/bash uidNumber: 2008 gidNumber: 2008 displayName: Testing email mail: [hidden email] objectClass: top objectClass: organizationalPerson objectClass: inetOrgPerson objectClass: posixAccount objectClass: inetmailrecipient uid: sw-apple cn: test email homeDirectory: /home/sw-apple maildrop: sw-apple@localhost mailacceptinggeneralid: [hidden email] And here is the script below. Some notes first:- - I have some LDAP entries with _no_ mail attribute and these query just fine with the script. - I haven't used any "free" methods yet. As I see it, by omitting the "free" methods, the script will not release memory from C objects. This doesn't matter in initial testing (or does it?). - You talked about adding an isNull method to Object. I tried inserting the code you gave as you can see at the top of the script. I've obviously done something wrong as it wouldn't compile so I commented it out. This has meant I've had to omit a line "vals isNull ifTrue: [". I'd like to get this working as a null in vals would trigger a crash like what I'm seeing I think (although as you can see the mail attribute does have a value). Do you know what would be causing the crash or have suggestions for something I can test? Thank you Stephen ------------------------ stephenw$ cat ldapapi.st #!/usr/local/bin/gst -f "call LDAP API" " CObject extend [ " "Will be in 3.1" " isNull [ ^address = 0 ] ] " CObject subclass: BER [ " ----------------------- LBER_F( void ) ber_free LDAP_P(( BerElement *ber, int freebuf )); ------------------------- " BER class >> free [ <cCall: 'ber_free' returning: #void args: #(#self #int)> ] ] CObject subclass: LDAPMessage [ LDAPMessage class >> free [ <cCall: 'ldap_msgfree' returning: #int args: #(#self)> ] ] CObject subclass: LDAP [ "class variables" scopeBase := 0. nolimit := 0. LDAP class >> openOn: host port: port [ "Maybe you prefer #open:port: as the selector, of course." <cCall: 'ldap_open' returning: #{LDAP} args: #(#string #int)> ] LDAP class >> on: host port: port [ "Maybe you prefer #init:port: as the selector, of course." <cCall: 'ldap_init' returning: #{LDAP} args: #(#string #int)> ] LDAP class >> errorString: resultNum [ "Utility methods might also go on the class side." <cCall: 'ldap_err2string' returning: #string args: #(#int)> ] simpleBindWithDN: who passwd: secret [ <cCall: 'ldap_simple_bind_s' returning: #int args: #(#self #string #string)> ] " ldap_search_ext_s C definition -------------- LDAP_F( int ) ldap_search_ext_s LDAP_P(( LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter, char **attrs, int attrsonly, LDAPControl **serverctrls, LDAPControl **clientctrls, struct timeval *timeout, int sizelimit, LDAPMessage **res )); rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE, ""(objectclass=*)"", NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &result --------------- " "TODO: write a wrapper method which has less parameters and calls this one" searchWithBase: baseDN scope: aScope filter: aFilter attributes: theAttribs isAttrsOnly: attrsOnly serverctrls: serverControl clientctrls: clientControl timeout: timeval sizelimit: numResults searchResult: aSearchResult [ <cCall: 'ldap_search_ext_s' returning: #int args: #(#self #string #int #string #cObject #int #cObject #cObject #cObject #int #cObjectPtr)> ] " ldap_first_entry C definition -------------- LDAP_F( LDAPMessage * ) ldap_first_entry LDAP_P(( LDAP *ld, LDAPMessage *chain )); --------------" firstEntry: searchResult [ <cCall: 'ldap_first_entry' returning: #{LDAPMessage} args: #(#self #cObject)> ] " ------------- LDAP_F( char * ) ldap_first_attribute LDAP_P(( LDAP *ld, LDAPMessage *entry, BerElement **ber )); -------------" firstAttribute: anEntry ber: aBer [ <cCall: 'ldap_first_attribute' returning: #cObject args: #(#self #cObject #cObjectPtr)> ] " ------------- LDAP_F( char * ) ldap_next_attribute LDAP_P(( LDAP *ld, LDAPMessage *entry, BerElement *ber )); -------------" nextAttribute: anEntry ber: aBer [ <cCall: 'ldap_next_attribute' returning: #cObject args: #(#self #cObject #cObject)> ] " ------------- #if LDAP_DEPRECATED LDAP_F( char ** ) ldap_get_values LDAP_P(( /* deprecated, use ldap_get_values_len */ LDAP *ld, LDAPMessage *entry, LDAP_CONST char *target )); -------------" getValues: anEntry attribute: anAttribute [ <cCall: 'ldap_get_values' returning: #{CString} args: #(#self #cObject #cObject)> ] ] | ldapcall ldap resultCode resultCode2 errorMsg hostName baseDN | Transcript showCr: 'started'. DLD addLibrary: 'libldap'. hostName := 'aserver'. baseDN := 'ou=Users,dc=example,dc=com'. superviserDN := 'uid=sw,ou=Users,dc=example,dc=com'. password := 'apwd'. ldap := LDAP openOn: hostName port: 389. ldap isNil ifTrue: [ File checkError. ObjectMemory quit: 1 ]. resultCode := ldap simpleBindWithDN: superviserDN passwd: password. (resultCode > 0) ifTrue: [ "Transcript showCr: ('%1 is result from bind' % { resultCode })." errorCodeMsg := LDAP errorString: resultCode. Transcript showCr: 'The LDAP Bind failed with the message: ',errorCodeMsg ]. (resultCode = 0) ifTrue: [ Transcript showCr: '... about to run the search.'. resultCode := ldap searchWithBase: superviserDN scope: 16r0000 filter: '(objectclass=*)' attributes: nil isAttrsOnly: 0 serverctrls: nil clientctrls: nil timeout: nil sizelimit: 0 searchResult: (searchResult := LDAPMessage new). Transcript showCr: '... search completed'. (resultCode > 0) ifTrue: [ "Transcript showCr: ('%1 is result from bind' % { resultCode })." errorCodeMsg := LDAP errorString: resultCode. Transcript showCr: 'The LDAP search failed with the message: ',errorCodeMsg ]. entry := ldap firstEntry: searchResult. entry isNil ifFalse: [ ('Found results with base DN: ', baseDN) displayNl. aPtr := ldap firstAttribute: entry ber: (ber := BER new). ('Found first attribute') displayNl. [ aPtr isNil ] whileFalse: [ attr := String fromCData: aPtr. "aPtr free." vals := ldap getValues: entry attribute: attr. val := vals. "vals isNull ifTrue: [ ... omitted since isNull method won't compile" attr printNl. [ val value isNil ] whileFalse: [ (attr->val value) printNl. val incr ]. "vals free. " aPtr := ldap nextAttribute: entry ber: ber. ] ] ] ! _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
> > " CObject extend [ " > "Will be in 3.1" > " isNull [ ^address = 0 ] should have been self address = 0, can you try like that? Paolo _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
Error is this now:-
stephenw$ ./ldapapi.st started ... about to run the search. ... search completed Found results with base DN: ou=Users,dc=example,dc=com Found first attribute Object: CString new: 1 "<0x103bb58>" error: Invalid argument CString(16r206130): must be a Boolean SystemExceptions.MustBeBoolean(Exception)>>signal (AnsiExcept.st:216) SystemExceptions.MustBeBoolean class(SystemExceptions.WrongClass class)>>signalOn:mustBe: (AnsiExcept.st:913) SystemExceptions.MustBeBoolean class>>signalOn: (AnsiExcept.st:989) CString(Object)>>mustBeBoolean (Object.st:1359) UndefinedObject>>executeStatements (ldapapi.st:177) The block of code handling attribute/values looks like this now:- [ aPtr isNil ] whileFalse: [ attr := String fromCData: aPtr. aPtr free. vals := ldap getValues: entry attribute: attr. vals isNull ifTrue: [ val := vals. attr printNl. [ val value isNil ] whileFalse: [ (attr->val value) printNl. val incr ]. "vals free. " ]. aPtr := ldap nextAttribute: entry ber: ber. ] The isNull method looks like this now:- CObject extend [ "Will be in 3.1" isNull [ self address = 0 ] ] Thank you Stephen _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
> The isNull method looks like this now:- > CObject extend [ > "Will be in 3.1" > isNull [ self address = 0 ] > ] ahem, "^self address = 0"... :-) Paolo _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
thats better, the method compiles, but the script still doesn't like
that method... stephenw$ ./ldapapi.st started ... about to run the search. ... search completed Found results with base DN: ou=Users,dc=shineglobal,dc=com Found first attribute Object: nil error: did not understand #isNull MessageNotUnderstood(Exception)>>signal (AnsiExcept.st:216) UndefinedObject(Object)>>doesNotUnderstand: #isNull (AnsiExcept.st:1484) UndefinedObject>>executeStatements (ldapapi.st:169) And just to compare if I comment out the "vals isNull ifTrue: [" line started ... about to run the search. ... search completed Found results with base DN: ou=Users,dc=example,dc=com Found first attribute 'givenName' 'givenName'->'Test --' 'sn' 'sn'->'email' 'loginShell' 'loginShell'->'/bin/bash' 'uidNumber' 'uidNumber'->'2008' 'gidNumber' 'gidNumber'->'2008' 'displayName' 'displayName'->'Testing email' 'mail' Object: nil error: did not understand #value MessageNotUnderstood(Exception)>>signal (AnsiExcept.st:216) UndefinedObject(Object)>>doesNotUnderstand: #value (AnsiExcept.st:1484) UndefinedObject>>executeStatements (ldapapi.st:172) Here is the isNull method... CObject extend [ "Will be in 3.1" isNull [ ^self address = 0 ] ] Thanks Stephen _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
> And just to compare if I comment out the "vals isNull ifTrue: [" line 1) it should have been "vals isNull ifFalse: [ ... ]" 2) and in 3.1 I also added a UndefinedObject extend [ isNul [ ^true ] ] you can use either "vals isNull ifFalse: [ ... ]" or "vals isNil ifFalse: [ ... ]". Paolo _______________________________________________ help-smalltalk mailing list [hidden email] http://lists.gnu.org/mailman/listinfo/help-smalltalk |
Free forum by Nabble | Edit this page |