001    /*
002     * Copyright 2008-2014 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2014 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk.examples;
022    
023    
024    
025    import java.io.OutputStream;
026    import java.io.Serializable;
027    import java.text.ParseException;
028    import java.util.LinkedHashMap;
029    import java.util.List;
030    
031    import com.unboundid.ldap.sdk.CompareRequest;
032    import com.unboundid.ldap.sdk.CompareResult;
033    import com.unboundid.ldap.sdk.LDAPConnection;
034    import com.unboundid.ldap.sdk.LDAPException;
035    import com.unboundid.ldap.sdk.ResultCode;
036    import com.unboundid.ldap.sdk.Version;
037    import com.unboundid.util.Base64;
038    import com.unboundid.util.LDAPCommandLineTool;
039    import com.unboundid.util.StaticUtils;
040    import com.unboundid.util.ThreadSafety;
041    import com.unboundid.util.ThreadSafetyLevel;
042    import com.unboundid.util.args.ArgumentException;
043    import com.unboundid.util.args.ArgumentParser;
044    
045    
046    
047    /**
048     * This class provides a simple tool that can be used to perform compare
049     * operations in an LDAP directory server.  All of the necessary information is
050     * provided using command line arguments.    Supported arguments include those
051     * allowed by the {@link LDAPCommandLineTool} class.  In addition, a set of at
052     * least two unnamed trailing arguments must be given.  The first argument
053     * should be a string containing the name of the target attribute followed by a
054     * colon and the assertion value to use for that attribute (e.g.,
055     * "cn:john doe").  Alternately, the attribute name may be followed by two
056     * colons and the base64-encoded representation of the assertion value
057     * (e.g., "cn::  am9obiBkb2U=").  Any subsequent trailing arguments will be the
058     * DN(s) of entries in which to perform the compare operation(s).
059     * <BR><BR>
060     * Some of the APIs demonstrated by this example include:
061     * <UL>
062     *   <LI>Argument Parsing (from the {@code com.unboundid.util.args}
063     *       package)</LI>
064     *   <LI>LDAP Command-Line Tool (from the {@code com.unboundid.util}
065     *       package)</LI>
066     *   <LI>LDAP Communication (from the {@code com.unboundid.ldap.sdk}
067     *       package)</LI>
068     * </UL>
069     */
070    @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
071    public final class LDAPCompare
072           extends LDAPCommandLineTool
073           implements Serializable
074    {
075      /**
076       * The serial version UID for this serializable class.
077       */
078      private static final long serialVersionUID = 719069383330181184L;
079    
080    
081    
082      // The argument parser for this tool.
083      private ArgumentParser parser;
084    
085    
086    
087      /**
088       * Parse the provided command line arguments and make the appropriate set of
089       * changes.
090       *
091       * @param  args  The command line arguments provided to this program.
092       */
093      public static void main(final String[] args)
094      {
095        final ResultCode resultCode = main(args, System.out, System.err);
096        if (resultCode != ResultCode.SUCCESS)
097        {
098          System.exit(resultCode.intValue());
099        }
100      }
101    
102    
103    
104      /**
105       * Parse the provided command line arguments and make the appropriate set of
106       * changes.
107       *
108       * @param  args       The command line arguments provided to this program.
109       * @param  outStream  The output stream to which standard out should be
110       *                    written.  It may be {@code null} if output should be
111       *                    suppressed.
112       * @param  errStream  The output stream to which standard error should be
113       *                    written.  It may be {@code null} if error messages
114       *                    should be suppressed.
115       *
116       * @return  A result code indicating whether the processing was successful.
117       */
118      public static ResultCode main(final String[] args,
119                                    final OutputStream outStream,
120                                    final OutputStream errStream)
121      {
122        final LDAPCompare ldapCompare = new LDAPCompare(outStream, errStream);
123        return ldapCompare.runTool(args);
124      }
125    
126    
127    
128      /**
129       * Creates a new instance of this tool.
130       *
131       * @param  outStream  The output stream to which standard out should be
132       *                    written.  It may be {@code null} if output should be
133       *                    suppressed.
134       * @param  errStream  The output stream to which standard error should be
135       *                    written.  It may be {@code null} if error messages
136       *                    should be suppressed.
137       */
138      public LDAPCompare(final OutputStream outStream, final OutputStream errStream)
139      {
140        super(outStream, errStream);
141      }
142    
143    
144    
145      /**
146       * Retrieves the name for this tool.
147       *
148       * @return  The name for this tool.
149       */
150      @Override()
151      public String getToolName()
152      {
153        return "ldapcompare";
154      }
155    
156    
157    
158      /**
159       * Retrieves the description for this tool.
160       *
161       * @return  The description for this tool.
162       */
163      @Override()
164      public String getToolDescription()
165      {
166        return "Process compare operations in LDAP directory server.";
167      }
168    
169    
170    
171      /**
172       * Retrieves the version string for this tool.
173       *
174       * @return  The version string for this tool.
175       */
176      @Override()
177      public String getToolVersion()
178      {
179        return Version.NUMERIC_VERSION_STRING;
180      }
181    
182    
183    
184      /**
185       * Retrieves the maximum number of unnamed trailing arguments that are
186       * allowed.
187       *
188       * @return  A negative value to indicate that any number of trailing arguments
189       *          may be provided.
190       */
191      @Override()
192      public int getMaxTrailingArguments()
193      {
194        return -1;
195      }
196    
197    
198    
199      /**
200       * Retrieves a placeholder string that may be used to indicate what kinds of
201       * trailing arguments are allowed.
202       *
203       * @return  A placeholder string that may be used to indicate what kinds of
204       *          trailing arguments are allowed.
205       */
206      @Override()
207      public String getTrailingArgumentsPlaceholder()
208      {
209        return "attr:value dn1 [dn2 [dn3 [...]]]";
210      }
211    
212    
213    
214      /**
215       * Adds the arguments used by this program that aren't already provided by the
216       * generic {@code LDAPCommandLineTool} framework.
217       *
218       * @param  parser  The argument parser to which the arguments should be added.
219       *
220       * @throws  ArgumentException  If a problem occurs while adding the arguments.
221       */
222      @Override()
223      public void addNonLDAPArguments(final ArgumentParser parser)
224             throws ArgumentException
225      {
226        // No additional named arguments are required, but we should save a
227        // reference to the argument parser.
228        this.parser = parser;
229      }
230    
231    
232    
233      /**
234       * Performs the actual processing for this tool.  In this case, it gets a
235       * connection to the directory server and uses it to perform the requested
236       * comparisons.
237       *
238       * @return  The result code for the processing that was performed.
239       */
240      @Override()
241      public ResultCode doToolProcessing()
242      {
243        // Make sure that at least two trailing arguments were provided, which will
244        // be the attribute value assertion and at least one entry DN.
245        final List<String> trailingArguments = parser.getTrailingArguments();
246        if (trailingArguments.isEmpty())
247        {
248          err("No attribute value assertion was provided.");
249          err();
250          err(parser.getUsageString(79));
251          return ResultCode.PARAM_ERROR;
252        }
253        else if (trailingArguments.size() == 1)
254        {
255          err("No target entry DNs were provided.");
256          err();
257          err(parser.getUsageString(79));
258          return ResultCode.PARAM_ERROR;
259        }
260    
261    
262        // Parse the attribute value assertion.
263        final String avaString = trailingArguments.get(0);
264        final int colonPos = avaString.indexOf(':');
265        if (colonPos <= 0)
266        {
267          err("Malformed attribute value assertion.");
268          err();
269          err(parser.getUsageString(79));
270          return ResultCode.PARAM_ERROR;
271        }
272    
273        final String attributeName = avaString.substring(0, colonPos);
274        final byte[] assertionValueBytes;
275        final int doubleColonPos = avaString.indexOf("::");
276        if (doubleColonPos == colonPos)
277        {
278          // There are two colons, so it's a base64-encoded assertion value.
279          try
280          {
281            assertionValueBytes = Base64.decode(avaString.substring(colonPos+2));
282          }
283          catch (ParseException pe)
284          {
285            err("Unable to base64-decode the assertion value:  ",
286                        pe.getMessage());
287            err();
288            err(parser.getUsageString(79));
289            return ResultCode.PARAM_ERROR;
290          }
291        }
292        else
293        {
294          // There is only a single colon, so it's a simple UTF-8 string.
295          assertionValueBytes =
296               StaticUtils.getBytes(avaString.substring(colonPos+1));
297        }
298    
299    
300        // Get the connection to the directory server.
301        final LDAPConnection connection;
302        try
303        {
304          connection = getConnection();
305          out("Connected to ", connection.getConnectedAddress(), ':',
306              connection.getConnectedPort());
307        }
308        catch (LDAPException le)
309        {
310          err("Error connecting to the directory server:  ", le.getMessage());
311          return le.getResultCode();
312        }
313    
314    
315        // For each of the target entry DNs, process the compare.
316        ResultCode resultCode = ResultCode.SUCCESS;
317        CompareRequest compareRequest = null;
318        for (int i=1; i < trailingArguments.size(); i++)
319        {
320          final String targetDN = trailingArguments.get(i);
321          if (compareRequest == null)
322          {
323            compareRequest = new CompareRequest(targetDN, attributeName,
324                                                assertionValueBytes);
325          }
326          else
327          {
328            compareRequest.setDN(targetDN);
329          }
330    
331          try
332          {
333            out("Processing compare request for entry ", targetDN);
334            final CompareResult result = connection.compare(compareRequest);
335            if (result.compareMatched())
336            {
337              out("The compare operation matched.");
338            }
339            else
340            {
341              out("The compare operation did not match.");
342            }
343          }
344          catch (LDAPException le)
345          {
346            resultCode = le.getResultCode();
347            err("An error occurred while processing the request:  ",
348                le.getMessage());
349            err("Result Code:  ", le.getResultCode().intValue(), " (",
350                le.getResultCode().getName(), ')');
351            if (le.getMatchedDN() != null)
352            {
353              err("Matched DN:  ", le.getMatchedDN());
354            }
355            if (le.getReferralURLs() != null)
356            {
357              for (final String url : le.getReferralURLs())
358              {
359                err("Referral URL:  ", url);
360              }
361            }
362          }
363          out();
364        }
365    
366    
367        // Close the connection to the directory server and exit.
368        connection.close();
369        out();
370        out("Disconnected from the server");
371        return resultCode;
372      }
373    
374    
375    
376      /**
377       * {@inheritDoc}
378       */
379      @Override()
380      public LinkedHashMap<String[],String> getExampleUsages()
381      {
382        final LinkedHashMap<String[],String> examples =
383             new LinkedHashMap<String[],String>();
384    
385        final String[] args =
386        {
387          "--hostname", "server.example.com",
388          "--port", "389",
389          "--bindDN", "uid=admin,dc=example,dc=com",
390          "--bindPassword", "password",
391          "givenName:John",
392          "uid=jdoe,ou=People,dc=example,dc=com"
393        };
394        final String description =
395             "Attempt to determine whether the entry for user " +
396             "'uid=jdoe,ou=People,dc=example,dc=com' has a value of 'John' for " +
397             "the givenName attribute.";
398        examples.put(args, description);
399    
400        return examples;
401      }
402    }