001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.tools;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.io.BufferedReader;
033    import java.io.FileReader;
034    import java.io.IOException;
035    import java.io.OutputStream;
036    import java.io.PrintStream;
037    import java.util.ArrayList;
038    import java.util.Iterator;
039    import java.util.LinkedHashSet;
040    import java.util.LinkedList;
041    
042    import org.opends.server.core.DirectoryServer;
043    import org.opends.server.extensions.ConfigFileHandler;
044    import org.opends.server.protocols.ldap.LDAPResultCode;
045    import org.opends.server.types.AttributeType;
046    import org.opends.server.types.DN;
047    import org.opends.server.types.Entry;
048    import org.opends.server.types.ExistingFileBehavior;
049    import org.opends.server.types.LDIFExportConfig;
050    import org.opends.server.types.LDIFImportConfig;
051    import org.opends.server.types.NullOutputStream;
052    import org.opends.server.types.ObjectClass;
053    import org.opends.server.types.SearchFilter;
054    import org.opends.server.types.SearchScope;
055    import org.opends.server.util.LDIFException;
056    import org.opends.server.util.LDIFReader;
057    import org.opends.server.util.LDIFWriter;
058    import org.opends.server.util.args.ArgumentException;
059    import org.opends.server.util.args.ArgumentParser;
060    import org.opends.server.util.args.BooleanArgument;
061    import org.opends.server.util.args.IntegerArgument;
062    import org.opends.server.util.args.MultiChoiceArgument;
063    import org.opends.server.util.args.StringArgument;
064    
065    import static org.opends.messages.ToolMessages.*;
066    import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
067    import static org.opends.server.util.StaticUtils.*;
068    import static org.opends.server.tools.ToolConstants.*;
069    
070    
071    
072    /**
073     * This class provides a program that may be used to search LDIF files.  It is
074     * modeled after the LDAPSearch tool, with the primary differencing being that
075     * all of its data comes from LDIF rather than communicating over LDAP.
076     * However, it does have a number of differences that allow it to perform
077     * multiple operations in a single pass rather than requiring multiple passes
078     * through the LDIF.
079     */
080    public class LDIFSearch
081    {
082      /**
083       * The fully-qualified name of this class.
084       */
085      private static final String CLASS_NAME = "org.opends.server.tools.LDIFSearch";
086    
087    
088    
089      /**
090       * The search scope string that will be used for baseObject searches.
091       */
092      private static final String SCOPE_STRING_BASE = "base";
093    
094    
095    
096      /**
097       * The search scope string that will be used for singleLevel searches.
098       */
099      private static final String SCOPE_STRING_ONE = "one";
100    
101    
102    
103      /**
104       * The search scope string that will be used for wholeSubtree searches.
105       */
106      private static final String SCOPE_STRING_SUB = "sub";
107    
108    
109    
110      /**
111       * The search scope string that will be used for subordinateSubtree searches.
112       */
113      private static final String SCOPE_STRING_SUBORDINATE = "subordinate";
114    
115    
116    
117      /**
118       * Provides the command line arguments to the <CODE>mainSearch</CODE> method
119       * so that they can be processed.
120       *
121       * @param  args  The command line arguments provided to this program.
122       */
123      public static void main(String[] args)
124      {
125        int exitCode = mainSearch(args, true, System.out, System.err);
126        if (exitCode != 0)
127        {
128          System.exit(filterExitCode(exitCode));
129        }
130      }
131    
132    
133    
134      /**
135       * Parses the provided command line arguments and performs the appropriate
136       * search operation.
137       *
138       * @param  args              The command line arguments provided to this
139       *                           program.
140       * @param  initializeServer  True if server initialization should be done.
141       * @param  outStream         The output stream to use for standard output, or
142       *                           {@code null} if standard output is not needed.
143       * @param  errStream         The output stream to use for standard error, or
144       *                           {@code null} if standard error is not needed.
145       *
146       * @return  The return code for this operation.  A value of zero indicates
147       *          that all processing completed successfully.  A nonzero value
148       *          indicates that some problem occurred during processing.
149       */
150      public static int mainSearch(String[] args, boolean initializeServer,
151                                   OutputStream outStream, OutputStream errStream)
152      {
153        PrintStream out;
154        if (outStream == null)
155        {
156          out = NullOutputStream.printStream();
157        }
158        else
159        {
160          out = new PrintStream(outStream);
161        }
162    
163        PrintStream err;
164        if (errStream == null)
165        {
166          err = NullOutputStream.printStream();
167        }
168        else
169        {
170          err = new PrintStream(errStream);
171        }
172    
173        LinkedHashSet<String> scopeStrings = new LinkedHashSet<String>(4);
174        scopeStrings.add(SCOPE_STRING_BASE);
175        scopeStrings.add(SCOPE_STRING_ONE);
176        scopeStrings.add(SCOPE_STRING_SUB);
177        scopeStrings.add(SCOPE_STRING_SUBORDINATE);
178    
179    
180        BooleanArgument     dontWrap;
181        BooleanArgument     overwriteExisting;
182        BooleanArgument     showUsage;
183        StringArgument      filterFile;
184        IntegerArgument     sizeLimit;
185        IntegerArgument     timeLimit;
186        MultiChoiceArgument scopeString;
187        StringArgument      baseDNString;
188        StringArgument      configClass;
189        StringArgument      configFile;
190        StringArgument      ldifFile;
191        StringArgument      outputFile;
192    
193    
194        Message toolDescription = INFO_LDIFSEARCH_TOOL_DESCRIPTION.get();
195        ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription,
196                                                      false, true, 0, 0,
197                                                      "[filter] [attributes ...]");
198    
199        try
200        {
201          ldifFile = new StringArgument(
202                  "ldiffile", 'l', "ldifFile", false, true,
203                  true, INFO_LDIFFILE_PLACEHOLDER.get(), null, null,
204                  INFO_LDIFSEARCH_DESCRIPTION_LDIF_FILE.get());
205          argParser.addArgument(ldifFile);
206    
207          baseDNString = new StringArgument(
208                  "basedn", OPTION_SHORT_BASEDN,
209                  OPTION_LONG_BASEDN, false, true,
210                  true, INFO_BASEDN_PLACEHOLDER.get(), "", null,
211                  INFO_LDIFSEARCH_DESCRIPTION_BASEDN.get());
212          argParser.addArgument(baseDNString);
213    
214          scopeString = new MultiChoiceArgument(
215                  "scope", 's', "searchScope", false, false,
216                  true, INFO_SCOPE_PLACEHOLDER.get(), SCOPE_STRING_SUB,
217                  null, scopeStrings, false,
218                  INFO_LDIFSEARCH_DESCRIPTION_SCOPE.get());
219          argParser.addArgument(scopeString);
220    
221          configFile = new StringArgument(
222                  "configfile", 'c', "configFile", false,
223                  false, true, INFO_CONFIGFILE_PLACEHOLDER.get(), null, null,
224                  INFO_DESCRIPTION_CONFIG_FILE.get());
225          configFile.setHidden(true);
226          argParser.addArgument(configFile);
227    
228          configClass = new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS,
229                                 OPTION_LONG_CONFIG_CLASS, false,
230                                 false, true, INFO_CONFIGCLASS_PLACEHOLDER.get(),
231                                 ConfigFileHandler.class.getName(), null,
232                                 INFO_DESCRIPTION_CONFIG_CLASS.get());
233          configClass.setHidden(true);
234          argParser.addArgument(configClass);
235    
236          filterFile = new StringArgument("filterfile", 'f', "filterFile", false,
237              false, true, INFO_FILTER_FILE_PLACEHOLDER.get(), null, null,
238              INFO_LDIFSEARCH_DESCRIPTION_FILTER_FILE.get());
239          argParser.addArgument(filterFile);
240    
241          outputFile = new StringArgument(
242                  "outputfile", 'o', "outputFile", false,
243                  false, true, INFO_OUTPUT_FILE_PLACEHOLDER.get(), null, null,
244                  INFO_LDIFSEARCH_DESCRIPTION_OUTPUT_FILE.get());
245          argParser.addArgument(outputFile);
246    
247          overwriteExisting =
248               new BooleanArgument(
249                       "overwriteexisting", 'O',"overwriteExisting",
250                       INFO_LDIFSEARCH_DESCRIPTION_OVERWRITE_EXISTING.get());
251          argParser.addArgument(overwriteExisting);
252    
253          dontWrap = new BooleanArgument(
254                  "dontwrap", 'T', "dontWrap",
255                  INFO_LDIFSEARCH_DESCRIPTION_DONT_WRAP.get());
256          argParser.addArgument(dontWrap);
257    
258          sizeLimit = new IntegerArgument(
259                  "sizelimit", 'z', "sizeLimit", false,
260                  false, true, INFO_SIZE_LIMIT_PLACEHOLDER.get(), 0, null,
261                  true, 0, false, 0,
262                  INFO_LDIFSEARCH_DESCRIPTION_SIZE_LIMIT.get());
263          argParser.addArgument(sizeLimit);
264    
265          timeLimit = new IntegerArgument(
266                  "timelimit", 't', "timeLimit", false,
267                  false, true, INFO_TIME_LIMIT_PLACEHOLDER.get(), 0, null,
268                  true, 0, false, 0,
269                  INFO_LDIFSEARCH_DESCRIPTION_TIME_LIMIT.get());
270          argParser.addArgument(timeLimit);
271    
272    
273          showUsage = new BooleanArgument(
274                  "help", OPTION_SHORT_HELP,
275                  OPTION_LONG_HELP,
276                  INFO_DESCRIPTION_USAGE.get());
277          argParser.addArgument(showUsage);
278          argParser.setUsageArgument(showUsage);
279        }
280        catch (ArgumentException ae)
281        {
282          Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
283          err.println(message);
284          return 1;
285        }
286    
287    
288        // Parse the command-line arguments provided to the program.
289        try
290        {
291          argParser.parseArguments(args);
292        }
293        catch (ArgumentException ae)
294        {
295          Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
296    
297          err.println(message);
298          err.println(argParser.getUsage());
299          return LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
300        }
301    
302    
303        // If we should just display usage or version information,
304        // then print it and exit.
305        if (argParser.usageOrVersionDisplayed())
306        {
307          return 0;
308        }
309    
310    
311        // Make sure that at least one filter was provided.  Also get the attribute
312        // list at the same time because it may need to be specified in the same
313        // way.
314        boolean            allUserAttrs        = false;
315        boolean            allOperationalAttrs = false;
316        //Return objectclass attribute unless analysis of the arguments determines
317        //otherwise.
318        boolean            includeObjectclassAttrs = true;
319        LinkedList<String> attributeNames;
320        LinkedList<String> objectClassNames    = new LinkedList<String>();
321        LinkedList<String> filterStrings = new LinkedList<String>();
322        if (filterFile.isPresent())
323        {
324          BufferedReader in = null;
325          try
326          {
327            String fileNameValue = filterFile.getValue();
328            in = new BufferedReader(new FileReader(fileNameValue));
329            String line = null;
330    
331            while ((line = in.readLine()) != null)
332            {
333              if(line.trim().equals(""))
334              {
335                // ignore empty lines.
336                continue;
337              }
338              filterStrings.add(line);
339            }
340          } catch(Exception e)
341          {
342            err.println(wrapText(e.getMessage(), MAX_LINE_WIDTH));
343            return 1;
344          }
345          finally
346          {
347            if(in != null)
348            {
349              try
350              {
351               in.close();
352              } catch (IOException ioe) {}
353            }
354          }
355    
356          ArrayList<String> trailingArguments = argParser.getTrailingArguments();
357          if ((trailingArguments == null) || trailingArguments.isEmpty())
358          {
359            attributeNames = new LinkedList<String>();
360          }
361          else
362          {
363            attributeNames = new LinkedList<String>();
364            for (String attributeName : trailingArguments)
365            {
366              String lowerName = toLowerCase(attributeName);
367              if (lowerName.equals("*"))
368              {
369                allUserAttrs = true;
370              }
371              else if (lowerName.equals("+"))
372              {
373                allOperationalAttrs = true;
374              }
375              else if (lowerName.startsWith("@"))
376              {
377                objectClassNames.add(lowerName.substring(1));
378              }
379              else
380              {
381                attributeNames.add(lowerName);
382              }
383            }
384          }
385        }
386        else
387        {
388          ArrayList<String> trailingArguments = argParser.getTrailingArguments();
389          if ((trailingArguments == null) || trailingArguments.isEmpty())
390          {
391            Message message = ERR_LDIFSEARCH_NO_FILTER.get();
392            err.println(message);
393            return 1;
394          }
395          else
396          {
397            Iterator<String> iterator = trailingArguments.iterator();
398    
399            filterStrings = new LinkedList<String>();
400            filterStrings.add(iterator.next());
401    
402            attributeNames = new LinkedList<String>();
403            while (iterator.hasNext())
404            {
405              String lowerName = toLowerCase(iterator.next());
406              if (lowerName.equals("*"))
407              {
408                allUserAttrs = true;
409              }
410              else if (lowerName.equals("+"))
411              {
412                allOperationalAttrs = true;
413              }
414              else if (lowerName.startsWith("@"))
415              {
416                objectClassNames.add(lowerName.substring(1));
417              }
418              else
419              {
420                attributeNames.add(lowerName);
421              }
422            }
423          }
424        }
425    
426        if (attributeNames.isEmpty() && objectClassNames.isEmpty() &&
427            (! allOperationalAttrs))
428        {
429          // This will be true if no attributes were requested, which is effectively
430          // all user attributes.  It will also be true if just "*" was included,
431          // but the net result will be the same.
432          allUserAttrs = true;
433        }
434    
435        //Determine if objectclass attribute should be returned.
436        if(!allUserAttrs) {
437          //Single '+', never return objectclass.
438          if(allOperationalAttrs && objectClassNames.isEmpty() &&
439             attributeNames.isEmpty())
440            includeObjectclassAttrs=false;
441          //If "objectclass" isn't specified in the attributes to return, then
442          //don't include objectclass attribiute.
443          if(!attributeNames.isEmpty() && objectClassNames.isEmpty() &&
444             !attributeNames.contains("objectclass"))
445             includeObjectclassAttrs=false;
446        }
447    
448    
449        // Bootstrap the Directory Server configuration for use as a client.
450        DirectoryServer directoryServer = DirectoryServer.getInstance();
451    
452        // If we're to use the configuration then initialize it, along with the
453        // schema.
454        boolean checkSchema = configFile.isPresent();
455    
456        if(initializeServer) {
457         DirectoryServer.bootstrapClient();
458    
459        if (checkSchema)
460        {
461          try
462          {
463            DirectoryServer.initializeJMX();
464          }
465          catch (Exception e)
466          {
467            Message message = ERR_LDIFSEARCH_CANNOT_INITIALIZE_JMX.get(
468                    String.valueOf(configFile.getValue()),
469                    e.getMessage());
470            err.println(message);
471            return 1;
472          }
473    
474          try
475          {
476            directoryServer.initializeConfiguration(configClass.getValue(),
477                                                    configFile.getValue());
478          }
479          catch (Exception e)
480          {
481            Message message = ERR_LDIFSEARCH_CANNOT_INITIALIZE_CONFIG.get(
482                    String.valueOf(configFile.getValue()),
483                    e.getMessage());
484            err.println(message);
485            return 1;
486          }
487    
488          try
489          {
490            directoryServer.initializeSchema();
491          }
492          catch (Exception e)
493          {
494            Message message = ERR_LDIFSEARCH_CANNOT_INITIALIZE_SCHEMA.get(
495                    String.valueOf(configFile.getValue()),
496                    e.getMessage());
497            err.println(message);
498            return 1;
499          }
500        }
501        }
502    
503        // Choose the desired search scope.
504        SearchScope searchScope;
505        if (scopeString.isPresent())
506        {
507          String scopeStr = toLowerCase(scopeString.getValue());
508          if (scopeStr.equals(SCOPE_STRING_BASE))
509          {
510            searchScope = SearchScope.BASE_OBJECT;
511          }
512          else if (scopeStr.equals(SCOPE_STRING_ONE))
513          {
514            searchScope = SearchScope.SINGLE_LEVEL;
515          }
516          else if (scopeStr.equals(SCOPE_STRING_SUBORDINATE))
517          {
518            searchScope = SearchScope.SUBORDINATE_SUBTREE;
519          }
520          else
521          {
522            searchScope = SearchScope.WHOLE_SUBTREE;
523          }
524        }
525        else
526        {
527          searchScope = SearchScope.WHOLE_SUBTREE;
528        }
529    
530    
531        // Create the list of filters that will be used to process the searches.
532        LinkedList<SearchFilter> searchFilters = new LinkedList<SearchFilter>();
533        for (String filterString : filterStrings)
534        {
535          try
536          {
537            searchFilters.add(SearchFilter.createFilterFromString(filterString));
538          }
539          catch (Exception e)
540          {
541            Message message = ERR_LDIFSEARCH_CANNOT_PARSE_FILTER.get(
542                    filterString, e.getMessage());
543            err.println(message);
544            return 1;
545          }
546        }
547    
548    
549        // Transform the attributes to return from strings to attribute types.
550        LinkedHashSet<AttributeType> userAttributeTypes =
551             new LinkedHashSet<AttributeType>();
552        LinkedHashSet<AttributeType> operationalAttributeTypes =
553             new LinkedHashSet<AttributeType>();
554        for (String attributeName : attributeNames)
555        {
556          AttributeType t = DirectoryServer.getAttributeType(attributeName, true);
557          if (t.isOperational())
558          {
559            operationalAttributeTypes.add(t);
560          }
561          else
562          {
563            userAttributeTypes.add(t);
564          }
565        }
566    
567        for (String objectClassName : objectClassNames)
568        {
569          ObjectClass c = DirectoryServer.getObjectClass(objectClassName, true);
570          for (AttributeType t : c.getRequiredAttributeChain())
571          {
572            if (t.isOperational())
573            {
574              operationalAttributeTypes.add(t);
575            }
576            else
577            {
578              userAttributeTypes.add(t);
579            }
580          }
581    
582          for (AttributeType t : c.getOptionalAttributeChain())
583          {
584            if (t.isOperational())
585            {
586              operationalAttributeTypes.add(t);
587            }
588            else
589            {
590              userAttributeTypes.add(t);
591            }
592          }
593        }
594    
595    
596        // Set the base DNs for the import config.
597        LinkedList<DN> baseDNs = new LinkedList<DN>();
598        if (baseDNString.isPresent())
599        {
600          for (String dnString : baseDNString.getValues())
601          {
602            try
603            {
604              baseDNs.add(DN.decode(dnString));
605            }
606            catch (Exception e)
607            {
608              Message message = ERR_LDIFSEARCH_CANNOT_PARSE_BASE_DN.get(
609                      dnString, e.getMessage());
610              err.println(message);
611              return 1;
612            }
613          }
614        }
615        else
616        {
617          baseDNs.add(DN.nullDN());
618        }
619    
620    
621        // Get the time limit in milliseconds.
622        long timeLimitMillis;
623        try
624        {
625          if (timeLimit.isPresent())
626          {
627            timeLimitMillis = 1000L * timeLimit.getIntValue();
628          }
629          else
630          {
631            timeLimitMillis = 0;
632          }
633        }
634        catch (Exception e)
635        {
636          Message message = ERR_LDIFSEARCH_CANNOT_PARSE_TIME_LIMIT.get(
637                  String.valueOf(e));
638          err.println(message);
639          return 1;
640        }
641    
642    
643        // Convert the size limit to an integer.
644        int sizeLimitValue;
645        try
646        {
647          if (sizeLimit.isPresent())
648          {
649            sizeLimitValue = sizeLimit.getIntValue();
650          }
651          else
652          {
653            sizeLimitValue =0;
654          }
655        }
656        catch (Exception e)
657        {
658          Message message = ERR_LDIFSEARCH_CANNOT_PARSE_SIZE_LIMIT.get(
659                  String.valueOf(e));
660          err.println(message);
661          return 1;
662        }
663    
664    
665        // Create the LDIF import configuration that will be used to read the source
666        // data.
667        LDIFImportConfig importConfig;
668        if (ldifFile.isPresent())
669        {
670          importConfig = new LDIFImportConfig(ldifFile.getValues());
671        }
672        else
673        {
674          importConfig = new LDIFImportConfig(System.in);
675        }
676    
677    
678        // Create the LDIF export configuration that will be used to write the
679        // matching entries.
680        LDIFExportConfig exportConfig;
681        if (outputFile.isPresent())
682        {
683          if (overwriteExisting.isPresent())
684          {
685            exportConfig = new LDIFExportConfig(outputFile.getValue(),
686                                                ExistingFileBehavior.OVERWRITE);
687          }
688          else
689          {
690            exportConfig = new LDIFExportConfig(outputFile.getValue(),
691                                                ExistingFileBehavior.APPEND);
692          }
693        }
694        else
695        {
696          exportConfig = new LDIFExportConfig(out);
697        }
698    
699        exportConfig.setIncludeObjectClasses(includeObjectclassAttrs);
700        if (dontWrap.isPresent())
701        {
702          exportConfig.setWrapColumn(0);
703        }
704        else
705        {
706          exportConfig.setWrapColumn(75);
707        }
708    
709    
710        // Create the LDIF reader/writer from the import/export config.
711        LDIFReader reader;
712        LDIFWriter writer;
713        try
714        {
715          reader = new LDIFReader(importConfig);
716        }
717        catch (Exception e)
718        {
719          Message message = ERR_LDIFSEARCH_CANNOT_CREATE_READER.get(
720                  String.valueOf(e));
721          err.println(message);
722          return 1;
723        }
724    
725        try
726        {
727          writer = new LDIFWriter(exportConfig);
728        }
729        catch (Exception e)
730        {
731          try
732          {
733            reader.close();
734          } catch (Exception e2) {}
735    
736          Message message = ERR_LDIFSEARCH_CANNOT_CREATE_WRITER.get(
737                  String.valueOf(e));
738          err.println(message);
739          return 1;
740        }
741    
742    
743        // Start reading data from the LDIF reader.
744        long startTime  = System.currentTimeMillis();
745        long stopTime   = startTime + timeLimitMillis;
746        long matchCount = 0;
747        int  resultCode = LDAPResultCode.SUCCESS;
748        while (true)
749        {
750          // If the time limit has been reached, then stop now.
751          if ((timeLimitMillis > 0) && (System.currentTimeMillis() > stopTime))
752          {
753            resultCode = LDAPResultCode.TIME_LIMIT_EXCEEDED;
754    
755            Message message = WARN_LDIFSEARCH_TIME_LIMIT_EXCEEDED.get();
756            err.println(message);
757            break;
758          }
759    
760    
761          try
762          {
763            Entry entry = reader.readEntry(checkSchema);
764            if (entry == null)
765            {
766              break;
767            }
768    
769    
770            // Check to see if the entry has an acceptable base and scope.
771            boolean matchesBaseAndScope = false;
772            for (DN baseDN : baseDNs)
773            {
774              if (entry.matchesBaseAndScope(baseDN, searchScope))
775              {
776                matchesBaseAndScope = true;
777                break;
778              }
779            }
780    
781            if (! matchesBaseAndScope)
782            {
783              continue;
784            }
785    
786    
787            // Check to see if the entry matches any of the filters.
788            boolean matchesFilter = false;
789            for (SearchFilter filter : searchFilters)
790            {
791              if (filter.matchesEntry(entry))
792              {
793                matchesFilter = true;
794                break;
795              }
796            }
797    
798            if (! matchesFilter)
799            {
800              continue;
801            }
802    
803    
804            // Prepare the entry to return to the client.
805            if (! allUserAttrs)
806            {
807              Iterator<AttributeType> iterator =
808                   entry.getUserAttributes().keySet().iterator();
809              while (iterator.hasNext())
810              {
811                if (! userAttributeTypes.contains(iterator.next()))
812                {
813                  iterator.remove();
814                }
815              }
816            }
817    
818            if (! allOperationalAttrs)
819            {
820              Iterator<AttributeType> iterator =
821                   entry.getOperationalAttributes().keySet().iterator();
822              while (iterator.hasNext())
823              {
824                if (! operationalAttributeTypes.contains(iterator.next()))
825                {
826                  iterator.remove();
827                }
828              }
829            }
830    
831    
832            // Write the entry to the client and increase the count.
833            // FIXME -- Should we include a comment about which base+filter matched?
834            writer.writeEntry(entry);
835            writer.flush();
836    
837            matchCount++;
838            if ((sizeLimitValue > 0) && (matchCount >= sizeLimitValue))
839            {
840              resultCode = LDAPResultCode.SIZE_LIMIT_EXCEEDED;
841    
842              Message message = WARN_LDIFSEARCH_SIZE_LIMIT_EXCEEDED.get();
843              err.println(message);
844              break;
845            }
846          }
847          catch (LDIFException le)
848          {
849            if (le.canContinueReading())
850            {
851              Message message = ERR_LDIFSEARCH_CANNOT_READ_ENTRY_RECOVERABLE.get(
852                      le.getMessage());
853              err.println(message);
854            }
855            else
856            {
857              Message message = ERR_LDIFSEARCH_CANNOT_READ_ENTRY_FATAL.get(
858                      le.getMessage());
859              err.println(message);
860              resultCode = LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
861              break;
862            }
863          }
864          catch (Exception e)
865          {
866            Message message = ERR_LDIFSEARCH_ERROR_DURING_PROCESSING.get(
867                    String.valueOf(e));
868            err.println(message);
869            resultCode = LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
870            break;
871          }
872        }
873    
874    
875        // Close the reader and writer.
876        try
877        {
878          reader.close();
879        } catch (Exception e) {}
880    
881        try
882        {
883          writer.close();
884        } catch (Exception e) {}
885    
886    
887        return resultCode;
888      }
889    }
890