1# Copyright (c) 2000-2004 Graham Barr <gbarr@pobox.com>. All rights reserved. 2# This program is free software; you can redistribute it and/or 3# modify it under the same terms as Perl itself. 4 5package Net::LDAP::Control::VLV; 6 7use vars qw(@ISA $VERSION); 8use Net::LDAP::Control; 9 10@ISA = qw(Net::LDAP::Control); 11$VERSION = "0.03"; 12 13use Net::LDAP::ASN qw(VirtualListViewRequest); 14use strict; 15 16sub init { 17 my($self) = @_; 18 19 # VLVREQUEST should always have a critical of true 20 $self->{'critical'} = 1 unless exists $self->{'critical'}; 21 22 if (exists $self->{value}) { 23 $self->value($self->{value}); 24 } 25 else { 26 my $asn = $self->{asn} = {}; 27 28 $asn->{beforeCount} = $self->{before} || 0; 29 $asn->{afterCount} = $self->{after} || 0; 30 if (exists $self->{assert}) { 31 $asn->{byValue} = $self->{assert}; 32 } 33 else { 34 $asn->{byoffset} = { 35 offset => $self->{offset} || 0, 36 contentCount => $self->{content} || 0 37 }; 38 } 39 } 40 41 $self; 42} 43 44sub before { 45 my $self = shift; 46 if (@_) { 47 delete $self->{value}; 48 return $self->{asn}{beforeCount} = shift; 49 } 50 $self->{asn}{beforeCount}; 51} 52 53sub after { 54 my $self = shift; 55 if (@_) { 56 delete $self->{value}; 57 return $self->{asn}{afterCount} = shift; 58 } 59 $self->{asn}{afterCount}; 60} 61 62sub content { 63 my $self = shift; 64 if (@_) { 65 delete $self->{value}; 66 if (exists $self->{asn}{byValue}) { 67 delete $self->{asn}{byValue}; 68 $self->{asn}{byoffset} = { offset => 0 }; 69 } 70 return $self->{asn}{byoffset}{contentCount} = shift; 71 } 72 exists $self->{asn}{byoffset} 73 ? $self->{asn}{byoffset}{contentCount} 74 : undef; 75} 76 77sub assert { 78 my $self = shift; 79 if (@_) { 80 delete $self->{value}; 81 delete $self->{asn}{byoffset}; 82 return $self->{asn}{byValue} = shift; 83 } 84 exists $self->{asn}{byValue} 85 ? $self->{asn}{byValue} 86 : undef; 87} 88 89sub context { 90 my $self = shift; 91 if (@_) { 92 delete $self->{value}; 93 return $self->{asn}{contextID} = shift; 94 } 95 $self->{asn}{contextID}; 96} 97 98# Update self with values from a response 99 100sub response { 101 my $self = shift; 102 my $resp = shift; 103 104 my $asn = $self->{asn}; 105 106 $asn->{contextID} = $resp->context; 107 $asn->{byoffset} = { 108 offset => $resp->target, 109 contentCount => $resp->content 110 }; 111 delete $asn->{byValue}; 112 113 1; 114} 115 116sub offset { 117 my $self = shift; 118 if (@_) { 119 delete $self->{value}; 120 if (exists $self->{asn}{byValue}) { 121 delete $self->{asn}{byValue}; 122 $self->{asn}{byoffset} = { contentCount => 0 }; 123 } 124 return $self->{asn}{byoffset}{offset} = shift; 125 } 126 exists $self->{asn}{byoffset} 127 ? $self->{asn}{byoffset}{offset} 128 : undef; 129} 130 131sub value { 132 my $self = shift; 133 134 if (@_) { 135 unless ($self->{asn} = $VirtualListViewRequest->decode($_[0])) { 136 delete $self->{value}; 137 return undef; 138 } 139 $self->{value} = shift; 140 } 141 142 exists $self->{value} 143 ? $self->{value} 144 : $self->{value} = $VirtualListViewRequest->encode($self->{asn}); 145} 146 147sub scroll { 148 my $self = shift; 149 my $n = shift; 150 my $asn = $self->{asn}; 151 my $byoffset = $asn->{byoffset} 152 or return undef; 153 my $offset = $byoffset->{offset} + $n; 154 my $content; 155 156 if ($offset < 1) { 157 $asn->{afterCount} += $asn->{beforeCount}; 158 $asn->{beforeCount} = 0; 159 $offset = $byoffset->{offset} = 1; 160 } 161 elsif ($byoffset->{contentCount} and $asn->{afterCount}+$offset >$byoffset->{contentCount}) { 162 if ($offset > $byoffset->{contentCount}) { 163 $offset = $byoffset->{offset} = $byoffset->{contentCount}; 164 $asn->{beforeCount} += $asn->{afterCount}; 165 $asn->{afterCount} = 0; 166 } 167 else { 168 my $tmp = $byoffset->{contentCount} - $offset; 169 $asn->{beforeCount} += $tmp; 170 $asn->{afterCount} -= $tmp; 171 $byoffset->{offset} = $offset; 172 } 173 } 174 else { 175 $byoffset->{offset} = $offset; 176 } 177 178 $offset; 179} 180 181sub scroll_page { 182 my $self = shift; 183 my $n = shift; 184 my $asn = $self->{asn}; 185 my $page_size = $asn->{beforeCount} + $asn->{afterCount} + 1; 186 187 $self->scroll( $page_size * $n); 188} 189 190sub start { 191 my $self = shift; 192 my $asn = $self->{asn}; 193 $asn->{afterCount} += $asn->{beforeCount}; 194 $asn->{beforeCount} = 0; 195 $self->offset(1); 196} 197 198sub end { 199 my $self = shift; 200 my $asn = $self->{asn}; 201 my $content = $self->content || 0; 202 203 $asn->{beforeCount} += $asn->{afterCount}; 204 $asn->{afterCount} = 0; 205 $self->offset($content); 206} 207 2081; 209 210__END__ 211 212=head1 NAME 213 214Net::LDAP::Control::VLV - LDAPv3 Virtual List View control object 215 216=head1 SYNOPSIS 217 218 use Net::LDAP; 219 use Net::LDAP::Control::VLV; 220 use Net::LDAP::Constant qw( LDAP_CONTROL_VLVRESPONSE ); 221 222 $ldap = Net::LDAP->new( "ldap.mydomain.eg" ); 223 224 # Get the first 20 entries 225 $vlv = Net::LDAP::Control::VLV->new( 226 before => 0, # No entries from before target entry 227 after => 19, # 19 entries after target entry 228 content => 0, # List size unknown 229 offset => 1, # Target entry is the first 230 ); 231 $sort = Net::LDAP::Control::Sort->new( order => 'cn' ); 232 233 @args = ( base => "o=Ace Industry, c=us", 234 scope => "subtree", 235 filter => "(objectClass=inetOrgPerson)", 236 callback => \&process_entry, # Call this sub for each entry 237 control => [ $vlv, $sort ], 238 ); 239 240 $mesg = $ldap->search( @args ); 241 242 # Get VLV response control 243 ($resp) = $mesg->control( LDAP_CONTROL_VLVRESPONSE ) or die; 244 $vlv->response( $resp ); 245 246 # Set the control to get the last 20 entries 247 $vlv->end; 248 249 $mesg = $ldap->search( @args ); 250 251 # Get VLV response control 252 ($resp) = $mesg->control( LDAP_CONTROL_VLVRESPONSE ) or die; 253 $vlv->response( $resp ); 254 255 # Now get the previous page 256 $vlv->scroll_page( -1 ); 257 258 $mesg = $ldap->search( @args ); 259 260 # Get VLV response control 261 ($resp) = $mesg->control( LDAP_CONTROL_VLVRESPONSE ) or die; 262 $vlv->response( $resp ); 263 264 # Now page with first entry starting with "B" in the middle 265 $vlv->before(9); # Change page to show 9 before 266 $vlv->after(10); # Change page to show 10 after 267 $vlv->assert("B"); # assert "B" 268 269 $mesg = $ldap->search( @args ); 270 271=head1 DESCRIPTION 272 273C<Net::LDAP::Control::VLV> provides an interface for the creation and 274manipulation of objects that represent the Virtual List View as described 275by draft-ietf-ldapext-ldapv3-vlv-03.txt. 276 277When using a Virtual List View control in a search, it must be accompanied by a sort 278control. See L<Net::LDAP::Control::Sort> 279 280=cut 281 282## 283## Need some blurb here to describe the VLV control. Maybe extract some simple 284## describtion from the draft RFC 285## 286 287=head1 CONSTRUCTOR ARGUMENTS 288 289In addition to the constructor arguments described in 290L<Net::LDAP::Control> the following are provided. 291 292=over 4 293 294=item after 295 296Set the number of entries the server should return from the list after 297the target entry. 298 299=item assert 300 301Set the assertion value user to locate the target entry. This value should 302be a legal value to compare with the first attribute in the sort control 303that is passed with the VLV control. The target entry is the first entry 304in the list which is greater than or equal the assert value. 305 306=item before 307 308Set the number of entries the server should return from the list before 309the target entry. 310 311=item content 312 313Set the number of entries in the list. On the first search this value 314should be set to zero. On subsequent searches it should be set to the 315length of the list, as returned by the server in the VLVResponse control. 316 317=item context 318 319Set the context identifier. On the first search this value should be 320set to zero. On subsequent searches it should be set to the context 321value returned by the server in the VLVResponse control. 322 323=item offset 324 325Set the offset of the target entry. 326 327=back 328 329=head2 METHODS 330 331As with L<Net::LDAP::Control> each constructor argument 332described above is also avaliable as a method on the object which will 333return the current value for the attribute if called without an argument, 334and set a new value for the attribute if called with an argument. 335 336The C<offset> and C<assert> attributes are mutually exclusive. Setting 337one or the other will cause previous values set by the other to 338be forgotten. The C<content> attribute is also associated with the 339C<offset> attribute, so setting C<assert> will cause any C<content> 340value to be forgotten. 341 342=over 4 343 344=item end 345 346Set the target entry to the end of the list. This method will change the C<before> 347and C<after> attributes so that the target entry is the last in the page. 348 349=item response VLV_RESPONSE 350 351Set the attributes in the control as per VLV_RESPONSE. VLV_RESPONSE should be a control 352of type L<Net::LDAP::Control::VLVResponse> returned 353from the server. C<response> will populate the C<context>, C<offset> and C<content> 354attibutes of the control with the values from VLV_RESPONSE. Because this sets the 355C<offset> attribute, any previous setting of the C<assert> attribute will be forgotten. 356 357=item scroll NUM 358 359Move the target entry by NUM entries. A positive NUM will move the target entry towards 360the end of the list and a negative NUM will move the target entry towards the 361start of the list. Returns the index of the new target entry, or C<undef> if the current target 362is identified by an assertion. 363 364C<scroll> may change the C<before> and C<after> attributes if the scroll value would 365cause the page to go off either end of the list. But the page size will be maintained. 366 367=item scroll_page NUM 368 369Scroll by NUM pages. This method simple calculates the current page size and calls 370C<scroll> with C<NUM * $page_size> 371 372=item start 373 374Set the target entry to the start of the list. This method will change the C<before> and C<after> 375attributes to the the target entry is the first entry in the page. 376 377=back 378 379=head1 SEE ALSO 380 381L<Net::LDAP>, 382L<Net::LDAP::Control>, 383L<Net::LDAP::Control::Sort>, 384L<Net::LDAP::Control::VLVResponse> 385 386=head1 AUTHOR 387 388Graham Barr E<lt>gbarr@pobox.comE<gt> 389 390Please report any bugs, or post any suggestions, to the perl-ldap mailing list 391E<lt>perl-ldap@perl.orgE<gt> 392 393=head1 COPYRIGHT 394 395Copyright (c) 2000-2004 Graham Barr. All rights reserved. This program is 396free software; you can redistribute it and/or modify it under the same 397terms as Perl itself. 398 399=cut 400