From c4f26fc6c4f0457ed214d6b30270810d26d81174 Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Mon, 20 Nov 2023 12:07:41 +0100 Subject: [PATCH] Add paged search support Current controls do now support the paged search as specified in the RFC2696 [1]. A new control is introduced to allow paged search. This is used in the requests and responses allowing to specify the page size and the cookie for the following search. 1. https://www.ietf.org/rfc/rfc2696.txt --- .../main/java/netscape/ldap/LDAPControl.java | 3 + .../controls/LDAPPagedResultsControl.java | 244 ++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 java-sdk/ldapjdk/src/main/java/netscape/ldap/controls/LDAPPagedResultsControl.java diff --git a/java-sdk/ldapjdk/src/main/java/netscape/ldap/LDAPControl.java b/java-sdk/ldapjdk/src/main/java/netscape/ldap/LDAPControl.java index a2deb9f..ac0f698 100644 --- a/java-sdk/ldapjdk/src/main/java/netscape/ldap/LDAPControl.java +++ b/java-sdk/ldapjdk/src/main/java/netscape/ldap/LDAPControl.java @@ -51,6 +51,7 @@ import netscape.ldap.ber.stream.BERTag; import netscape.ldap.client.JDAPBERTagDecoder; import netscape.ldap.controls.LDAPEntryChangeControl; +import netscape.ldap.controls.LDAPPagedResultsControl; import netscape.ldap.controls.LDAPPasswordExpiredControl; import netscape.ldap.controls.LDAPPasswordExpiringControl; import netscape.ldap.controls.LDAPSortControl; @@ -534,6 +535,8 @@ public String toString() { LDAPSortControl.class ); LDAPControl.register( LDAPVirtualListResponse.VIRTUALLISTRESPONSE, LDAPVirtualListResponse.class ); + LDAPControl.register( LDAPPagedResultsControl.PAGEDSEARCH, + LDAPPagedResultsControl.class ); } catch (LDAPException e) { } } diff --git a/java-sdk/ldapjdk/src/main/java/netscape/ldap/controls/LDAPPagedResultsControl.java b/java-sdk/ldapjdk/src/main/java/netscape/ldap/controls/LDAPPagedResultsControl.java new file mode 100644 index 0000000..ef83ded --- /dev/null +++ b/java-sdk/ldapjdk/src/main/java/netscape/ldap/controls/LDAPPagedResultsControl.java @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +package netscape.ldap.controls; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import netscape.ldap.LDAPControl; +import netscape.ldap.LDAPException; +import netscape.ldap.ber.stream.BERElement; +import netscape.ldap.ber.stream.BERInteger; +import netscape.ldap.ber.stream.BEROctetString; +import netscape.ldap.ber.stream.BERSequence; +import netscape.ldap.client.JDAPBERTagDecoder; + +/** + * Represents an LDAP v3 server control that specifies a simple pagd result + * manipulation, which allows your LDAP client to get entries in multiple chunks + * (The OID for this control is 1.2.840.113556.1.4.319). + *

+ * + * To use paged search you create a "paged search" control that specifies + * the page size and the cookie from last search. You include the control in a + * search request. When a search is performed only a page is returned to the client + * and a new search has to be performed to access the following elements. + *

+ * + * + * When constructing an LDAPPagedResultsControl object, + * you can specify the following information: + *

+ * + *

+ *

+ * + * For example: + *

+ * ...
+ *      LDAPConnection ld = new LDAPConnection();
+ * 
+ *      try {
+ *          // Connect to server.
+ *          ld.connect(3,"ds.example.com", 3389, "cn=Directory Manager", "Secret.123");
+ *
+ *          LDAPPagedResultsControl pagecon = new LDAPPagedResultsControl(false, 3);
+ *          // Set the search constraints to use that control.
+ *          LDAPSearchConstraints cons = new LDAPSearchConstraints();
+ *          cons.setBatchSize(1);
+ *          cons.setServerControls(pagecon);
+ * 
+ *          // Start the paged search.
+ *          String cookie = null;
+ *          int pag = 1;
+ *          do{
+ *              LDAPSearchResults res = ld.search("ou=certificateRepository,ou=ca,dc=ca,dc=pki,dc=example,dc=com",
+ *                      LDAPv3.SCOPE_SUB, "(objectClass=certificateRecord)", null, false, cons);
+ * 
+ *              // Loop through the incoming results.
+ *              while (res.hasMoreElements()) {
+ *                  LDAPEntry entry = res.next();
+ *                  System.out.println("DN: " + entry.getDN());
+ *              }
+ *              for (LDAPControl c: res.getResponseControls()){
+ *                  if(c.getID().equals(LDAPPagedResultsControl.PAGEDSEARCH)){
+ *                      LDAPPagedResultsControl resC = (LDAPPagedResultsControl) c;
+ *                      cookie = resC.getCookie();
+ *                      System.out.println("The control for pag " + pag + " return a total or " + resC.getPageSize() + " and cookie " + resC.getCookie());
+ *                      if(cookie!=null){
+ *                          pagecon = new LDAPPagedResultsControl(false, 3, cookie);
+ *                          cons.setServerControls(pagecon);
+ *                      }
+ *                  }
+ *              }
+ *              pag++;
+ *          } while (cookie != null && !cookie.isEmpty());
+ *      } catch (Exception e) {
+ *          e.printStackTrace();
+ *      }
+ * 
+ * + * + * + * @see netscape.ldap.LDAPControl + * + * @author Marco Fargetta + */ +public class LDAPPagedResultsControl extends LDAPControl { + public final static String PAGEDSEARCH = "1.2.840.113556.1.4.319"; + private int m_pageSize; + private String m_cookie; + + /** + * Constructs an LDAPPagedResultsControl object + * that specifies a paged search. + * + * @param oid the oid of this control + * @param critical true if this control is critical to the search + * @param value the value associated with this control + * + * @see netscape.ldap.LDAPcontrol + * + * @throws LDAPException + * @throws IOException If value contains an invalid BER sequence. + * + * @see netscape.ldap.LDAPControl#register + */ + public LDAPPagedResultsControl(String oid, boolean critical, byte[] vals) + throws LDAPException, IOException { + super(oid, critical, vals); + if(!oid.equals(PAGEDSEARCH)) { + throw new LDAPException("oid must be LDAPPagedResultsControl.PAGEDSEARCH", + LDAPException.PARAM_ERROR); + } + + ByteArrayInputStream inStream = new ByteArrayInputStream( vals ); + JDAPBERTagDecoder decoder = new JDAPBERTagDecoder(); + int[] nRead = { 0 }; + + /* A sequence */ + BERSequence seq = (BERSequence) BERElement.getElement(decoder, inStream, nRead); + m_pageSize = ((BERInteger)seq.elementAt(0)).getValue(); + if(seq.size() == 1) { + return; + } + BEROctetString t = (BEROctetString)seq.elementAt(1); + byte[] cookie = t.getValue(); + m_cookie = cookie == null ? null : new String(t.getValue(), StandardCharsets.UTF_8); + } + + /** + * Constructs an LDAPPagedResultsControl object without a cookie. + * + * This is equivalent to + * LDAPPagedResultsControl(critical, pageSize, null) + * + * @param critical true if this control is critical to the search + * @param pageSize the number of entries to be returned with the following + * search request + * + * @see netscape.ldap.LDAPControl + */ + public LDAPPagedResultsControl(boolean critical, int pageSize) { + this(critical, pageSize, null); + } + + /** + * Constructs an LDAPPagedResultsControl. + * + * @param critical true if this control is critical to the search + * @param pageSize the number of entries to be returned with the following + * search request + * @param cookie The cookie to access the next entries. This is an opaque value + * returned by the server or null for the initial search + * + * @see netscape.ldap.LDAPControl + */ + public LDAPPagedResultsControl(boolean critical, int pageSize, String cookie) { + super(PAGEDSEARCH, critical, null); + this.m_pageSize = pageSize; + this.m_cookie = cookie; + m_value = generateNextPageValues(pageSize, cookie); + } + + /** + * Encode the parameters as requested for the control + * + * @param pageSize the number of entries to be returned with the following + * search request + * @param cookie The cookie to access the next entries (it is an opaque value + * returned by the server) + * @return + */ + private byte[] generateNextPageValues(int pageSize, String cookie) { + + BERSequence seq = new BERSequence(); + seq.addElement(new BERInteger(pageSize)); + if(cookie != null) { + seq.addElement(new BEROctetString(cookie)); + } else { + seq.addElement(new BEROctetString("")); + } + BEROctetString controlValue = new BEROctetString(flattenBER(seq)); + return controlValue.getValue(); + } + + /** + * Gets the page size for the search request. + * + * @return the number of entries to be returned when a search is requested to the server and + * the number of entries available when returned from the server. + */ + public int getPageSize() { + return m_pageSize; + } + + /** + * Gets the cookie for the following search request. + * + * @return the cookie to use for the following request or null if all entries + * have been returned. + */ + public String getCookie() { + return m_cookie; + } +}