Calling LDAP C API from Smalltalk

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

Calling LDAP C API from Smalltalk

Stephen-71
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
Reply | Threaded
Open this post in threaded view
|

Re: Calling LDAP C API from Smalltalk

Paolo Bonzini-2

> 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
Reply | Threaded
Open this post in threaded view
|

Re: Calling LDAP C API from Smalltalk

Paolo Bonzini-2

>       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
Reply | Threaded
Open this post in threaded view
|

Re: Calling LDAP C API from Smalltalk

Stephen-71
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
Reply | Threaded
Open this post in threaded view
|

Re: Re: Calling LDAP C API from Smalltalk

Paolo Bonzini-2
> 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
Reply | Threaded
Open this post in threaded view
|

Re: Re: Calling LDAP C API from Smalltalk

Stephen-71
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
Reply | Threaded
Open this post in threaded view
|

Re: Re: Calling LDAP C API from Smalltalk

Paolo Bonzini-2

>
> "  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
Reply | Threaded
Open this post in threaded view
|

Re: Re: Calling LDAP C API from Smalltalk

Stephen-71
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
Reply | Threaded
Open this post in threaded view
|

Re: Re: Calling LDAP C API from Smalltalk

Paolo Bonzini-2

> 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
Reply | Threaded
Open this post in threaded view
|

Re: Re: Calling LDAP C API from Smalltalk

Stephen-71
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
Reply | Threaded
Open this post in threaded view
|

Re: Re: Calling LDAP C API from Smalltalk

Paolo Bonzini-2

> 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