Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Phone lookup on Exchange server

by jlf (Scribe)
on Feb 13, 2002 at 01:01 UTC ( #145036=perlquestion: print w/ replies, xml ) Need Help??
jlf has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monks,

Our company's phone list is accessible through our internal web server, but the UI is suboptimal in that it requires you to search by surname and wastes time by forcing you through multiple web forms, just to find a phone number.

I set out to make a friendlier telephone number lookup function that would be accessible from the command line. The result is below, and permits case-insensitive substring searches of users' names.

The reason I'm posting in SoPW is that I think my implementation is rather inefficient because I'm using Win32::NetAdmin to enumerate users and Net::LDAP to get details about matching users -- it seems that Net::LDAP ought to be able to do it all but I can't figure out how to make it work. Any help in this regard as well as general thoughts about style or optimization are appreciated.


#!perl -w use strict; use Win32::NetAdmin qw(GetUsers FILTER_NORMAL_ACCOUNT); use Net::LDAP; # define host name of Exchange server use constant EXCHANGE_SERVER => "exchsrv1"; # define starting point of search in LDAP hierarchy use constant SEARCH_BASE => "cn=Recipients,ou=OURSITE,o=OURORG"; # specify substring of name to find on command line my $searchstring = shift or die "usage: $0 <substring>\n\t(<substring>=part of name to +seek)\n"; my (%rawusers, %matchingusers); # temporary hashes my @output; # final list of matching names & phone + numbers # next section retrieves a list of all users on the Exchange # server and saves entries matching our search into a new hash GetUsers(EXCHANGE_SERVER, FILTER_NORMAL_ACCOUNT, \%rawusers) or die "failed to enumerate users: $!"; while (my ($key, $value) = each %rawusers) { if ($value =~ /$searchstring/i) { $matchingusers{$key}=$value; } } # next section retrieves each matching user's particulars using # LDAP and pushes the result onto an array using a callback my $ldap = Net::LDAP->new(EXCHANGE_SERVER) or die "$@"; $ldap->bind( version => 3); foreach (keys %matchingusers) { my $search = $ldap->search( base => SEARCH_BASE, filter => "rdn=$_", callback => \&callback, ); } print sort @output; # sort output by first name sub callback { my ($search, $entry) = @_; $entry = $search->shift_entry; my @results=(); # Note: named parameters of get_value() may vary -- # use perldoc Net::LDAP for more info if (defined $entry and $entry->get_value("telephonenumber")) { my $first = $entry->get_value("givenname"); my $last = $entry->get_value("sn"); my $phone = $entry->get_value("telephonenumber"); if (defined $first) { # avoid some weird ent +ries push @output, "$first $last: $phone\n"; } } }

Comment on Phone lookup on Exchange server
Download Code
Re: Phone lookup on Exchange server
by mattr (Curate) on Feb 13, 2002 at 09:17 UTC
    Have to say I haven't done much LDAP but just looking at the module I'm wondering if you tried the Net::LDAP::Filter object in the perl-ldap bundle. Seems to have a lot of flexibility, perhaps you can just feed it that normal users constant you have.. Can you post what you tried to do and why you failed?

    Perhaps it would be good to sneak onto the line and listen to what is going between the clients and the server. In unix I might use tcpdump, or perhaps you have an LDAP server in debug mode.. Sorry I can't be of more help.

    Move SIG!

      I did perldoc Net::LDAP::Filter but I'm afraid it was a bit too opaque for me, being an utter LDAP newbie.

      Some of my attempts to enumerate users with LDAP were:

      1. SEARCH_BASE="ou=OURSITE,o=OURORG", $filter="CN=Recipients" 2. SEARCH_BASE="ou=OURSITE,o=OURORG",$filter="cn=" 3. SEARCH_BASE="ou=OURSITE,o=OURORG",$filter="cn=*" 4. SEARCH_BASE="cn=Recipients,ou=OURSITE,o=OURORG", $filter = "" 5. SEARCH_BASE="cn=Recipients,ou=OURSITE,o=OURORG", $filter = FILTER_N +ORMAL_USERS
      All of these returned no results.

      Thanks for the suggestions. It sounds like I need to do some more hunting whereever Exchange admins congregate...


Re: Phone lookup on Exchange server
by jbisbee (Pilgrim) on Feb 13, 2002 at 16:51 UTC
    I started on something for CPAN that I never got around to finishing. I started this because I wanted to create a mutt alias file that contained everyone in the company by accessing Exchange via LDAP. Here are the basics.

    A sample calling program:

    #!/usr/bin/perl -w use strict; use Net::LDAP::Exchange; use Data::Dumper; use constant SERVER => 'Exchange Server Here'; use constant DOMAIN => 'Domain Here'; # If you need to login... use constant LOGIN => 'Exchange Login Here'; use constant PASSWORD => 'Password Here'; my $ex = Net::LDAP::Exchange->new(SERVER,DOMAIN,LOGIN,PASSWORD); my $result_set = $ex->search_lastname('Bisbee'); print Dumper($result_set);

    Here is the skelton of my module to search by last name:

    package Net::LDAP::Exchange; use strict; use Net::LDAP; use Data::Dumper; sub new { my ($class, $server, $domain, $name, $password) = @_; die 'server and domain are required' unless $server and $domain; die 'missing password' if $name and !$password; die 'missing name' if $password and !$name; my $self = bless {}, $class; $self->{ldap} = Net::LDAP->new($server); $self->{server} = $server; $self->{domain} = $domain; $self->{name} = $name if $name; $self->{password} = $password if $password; return $self; } sub search_lastname { my ($self,$search) = @_; my $query = _build_query($search,qw(CN)); my $result_set = $self->search($query); return $result_set; } sub search { my ($self,$query) = @_; if ($self->{name} and $self->{password}) { $self->{ldap}->bind('cn='.$self->{name}, password=>$self->{password}, version=>3); } else { $self->{ldap}->bind; } my $results = $self->{ldap}->search(base => 'o=' . $self->{domain}, filter => "(|$query)"); $results->code && die $results->error; my @result_set = (); foreach my $entry ($results->all_entries) { my @attributes = $entry->attributes; my $result = {}; for my $att (@attributes) { my $return_att = uc $att; $result->{$return_att} = $entry->get_value($att); } push @result_set, $result; } $self->{ldap}->unbind; return \@result_set; } sub _build_query { my ($search,@fields) = @_; my $query = join '', map { "($_=$search*) " } @fields; return $query; }
    If you have any comments or suggestions, send them to jbisbee at yahoo dot com.



      I see that your module searches by surname -- but how did you obtain your exhaustive list of Exchange users? (Or did you?) I had to resort to Win32::NetAdmin for that but it seems like there should be some LDAP query that accomplishes the same result.


        There is a max limit you can query on with LDAP (at least the exchange server that I played with did). I just queried on "A" through "Z" and figured that would work. Test this against your current list and make sure you get everyone.
        sub get_company_list { my ($self) = @_; my @company_list = (); for my $search ("A".."Z") { push @company_list, @{$self->search_lastname($search)}; } return \@company_list; }

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://145036]
Approved by root
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (9)
As of 2014-07-23 05:17 GMT
Find Nodes?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:

    Results (133 votes), past polls