001/*
002 * Copyright 2010-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2010-2019 Ping Identity Corporation
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 */
021package com.unboundid.ldap.listener;
022
023
024
025import java.net.Socket;
026import java.util.Arrays;
027import java.util.List;
028import java.util.logging.Handler;
029import java.util.logging.Level;
030import java.util.logging.LogRecord;
031
032import com.unboundid.asn1.ASN1OctetString;
033import com.unboundid.ldap.protocol.AbandonRequestProtocolOp;
034import com.unboundid.ldap.protocol.AddRequestProtocolOp;
035import com.unboundid.ldap.protocol.AddResponseProtocolOp;
036import com.unboundid.ldap.protocol.BindRequestProtocolOp;
037import com.unboundid.ldap.protocol.BindResponseProtocolOp;
038import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
039import com.unboundid.ldap.protocol.CompareResponseProtocolOp;
040import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
041import com.unboundid.ldap.protocol.DeleteResponseProtocolOp;
042import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
043import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp;
044import com.unboundid.ldap.protocol.IntermediateResponseProtocolOp;
045import com.unboundid.ldap.protocol.LDAPMessage;
046import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
047import com.unboundid.ldap.protocol.ModifyResponseProtocolOp;
048import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
049import com.unboundid.ldap.protocol.ModifyDNResponseProtocolOp;
050import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
051import com.unboundid.ldap.protocol.SearchResultDoneProtocolOp;
052import com.unboundid.ldap.protocol.SearchResultEntryProtocolOp;
053import com.unboundid.ldap.protocol.SearchResultReferenceProtocolOp;
054import com.unboundid.ldap.protocol.UnbindRequestProtocolOp;
055import com.unboundid.ldap.sdk.Control;
056import com.unboundid.ldap.sdk.Entry;
057import com.unboundid.ldap.sdk.LDAPException;
058import com.unboundid.ldap.sdk.ResultCode;
059import com.unboundid.ldif.LDIFModifyChangeRecord;
060import com.unboundid.util.NotMutable;
061import com.unboundid.util.ObjectPair;
062import com.unboundid.util.StaticUtils;
063import com.unboundid.util.ThreadSafety;
064import com.unboundid.util.ThreadSafetyLevel;
065import com.unboundid.util.Validator;
066
067
068
069/**
070 * This class provides a request handler that may be used to write detailed
071 * information about the contents of all requests and responses that pass
072 * through it.  It will be also be associated with another request handler that
073 * will actually be used to handle the request.
074 */
075@NotMutable()
076@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
077public final class LDAPDebuggerRequestHandler
078       extends LDAPListenerRequestHandler
079       implements IntermediateResponseTransformer, SearchEntryTransformer,
080                  SearchReferenceTransformer
081{
082  /**
083   * The thread-local buffers that will be used to hold the log messages as they
084   * are being generated.
085   */
086  private static final ThreadLocal<StringBuilder> BUFFERS = new ThreadLocal<>();
087
088
089
090  // The log handler that will be used to log the messages.
091  private final Handler logHandler;
092
093  // The request handler that actually will be used to process any requests
094  // received.
095  private final LDAPListenerRequestHandler requestHandler;
096
097  // The header string that will be used before each message.
098  private final String headerString;
099
100
101
102  /**
103   * Creates a new LDAP debugger request handler that will write detailed
104   * information about the contents of all requests and responses that pass
105   * through it using the provided log handler, and will process client requests
106   * using the provided request handler.
107   *
108   * @param  logHandler      The log handler that will be used to write detailed
109   *                         information about requests and responses.  Note
110   *                         that all messages will be logged at the INFO level.
111   *                         It must not be {@code null}.  Note that the log
112   *                         handler will not be automatically closed when the
113   *                         associated listener is shut down.
114   * @param  requestHandler  The request handler that will actually be used to
115   *                         process any requests received.  It must not be
116   *                         {@code null}.
117   */
118  public LDAPDebuggerRequestHandler(final Handler logHandler,
119              final LDAPListenerRequestHandler requestHandler)
120  {
121    Validator.ensureNotNull(logHandler, requestHandler);
122
123    this.logHandler     = logHandler;
124    this.requestHandler = requestHandler;
125
126    headerString = null;
127  }
128
129
130
131  /**
132   * Creates a new LDAP debugger request handler that will write detailed
133   * information about the contents of all requests and responses that pass
134   * through it using the provided log handler, and will process client requests
135   * using the provided request handler.
136   *
137   * @param  logHandler      The log handler that will be used to write detailed
138   *                         information about requests and responses.  Note
139   *                         that all messages will be logged at the INFO level.
140   *                         It must not be {@code null}.
141   * @param  requestHandler  The request handler that will actually be used to
142   *                         process any requests received.  It must not be
143   *                         {@code null}.
144   * @param  headerString    The string that should be given as the first line
145   *                         of every log message.
146   */
147  private LDAPDebuggerRequestHandler(final Handler logHandler,
148               final LDAPListenerRequestHandler requestHandler,
149               final String headerString)
150  {
151    Validator.ensureNotNull(logHandler, requestHandler);
152
153    this.logHandler     = logHandler;
154    this.requestHandler = requestHandler;
155    this.headerString    = headerString;
156  }
157
158
159
160  /**
161   * {@inheritDoc}
162   */
163  @Override()
164  public LDAPDebuggerRequestHandler newInstance(
165              final LDAPListenerClientConnection connection)
166         throws LDAPException
167  {
168    final StringBuilder b = getBuffer();
169    final Socket s = connection.getSocket();
170    b.append("conn=");
171    b.append(connection.getConnectionID());
172    b.append(" from=\"");
173    b.append(s.getInetAddress().getHostAddress());
174    b.append(':');
175    b.append(s.getPort());
176    b.append("\" to=\"");
177    b.append(s.getLocalAddress().getHostAddress());
178    b.append(':');
179    b.append(s.getLocalPort());
180    b.append('"');
181    b.append(StaticUtils.EOL);
182
183    final String header = b.toString();
184
185    final LDAPDebuggerRequestHandler h = new LDAPDebuggerRequestHandler(
186         logHandler, requestHandler.newInstance(connection), header);
187
188    connection.addIntermediateResponseTransformer(h);
189    connection.addSearchEntryTransformer(h);
190    connection.addSearchReferenceTransformer(h);
191
192    logHandler.publish(new LogRecord(Level.INFO, "CONNECT " + header));
193
194    return h;
195  }
196
197
198
199  /**
200   * {@inheritDoc}
201   */
202  @Override()
203  public void closeInstance()
204  {
205    final StringBuilder b = getBuffer();
206    b.append("DISCONNECT ");
207    b.append(headerString);
208
209    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
210
211    requestHandler.closeInstance();
212  }
213
214
215
216  /**
217   * {@inheritDoc}
218   */
219  @Override()
220  public void processAbandonRequest(final int messageID,
221                                    final AbandonRequestProtocolOp request,
222                                    final List<Control> controls)
223  {
224    final StringBuilder b = getBuffer();
225    appendHeader(b, messageID);
226
227    b.append("     Abandon Request Protocol Op:").append(StaticUtils.EOL);
228    b.append("          ID to Abandon:  ").append(request.getIDToAbandon()).
229         append(StaticUtils.EOL);
230
231    appendControls(b, controls);
232    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
233
234    requestHandler.processAbandonRequest(messageID, request, controls);
235  }
236
237
238
239  /**
240   * {@inheritDoc}
241   */
242  @Override()
243  public LDAPMessage processAddRequest(final int messageID,
244                                       final AddRequestProtocolOp request,
245                                       final List<Control> controls)
246  {
247    final StringBuilder b = getBuffer();
248    appendHeader(b, messageID);
249
250    b.append("     Add Request Protocol Op:").append(StaticUtils.EOL);
251
252    final Entry e = new Entry(request.getDN(), request.getAttributes());
253    final String[] ldifLines = e.toLDIF(80);
254    for (final String line : ldifLines)
255    {
256      b.append("          ").append(line).append(StaticUtils.EOL);
257    }
258
259    appendControls(b, controls);
260    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
261
262    final LDAPMessage responseMessage = requestHandler.processAddRequest(
263         messageID, request, controls);
264
265    b.setLength(0);
266    appendHeader(b, responseMessage.getMessageID());
267    b.append("     Add Response Protocol Op:").append(StaticUtils.EOL);
268
269    final AddResponseProtocolOp protocolOp =
270         responseMessage.getAddResponseProtocolOp();
271    appendResponse(b, protocolOp.getResultCode(),
272         protocolOp.getDiagnosticMessage(),
273         protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
274
275    appendControls(b, responseMessage.getControls());
276    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
277
278    return responseMessage;
279  }
280
281
282
283  /**
284   * {@inheritDoc}
285   */
286  @Override()
287  public LDAPMessage processBindRequest(final int messageID,
288                                        final BindRequestProtocolOp request,
289                                        final List<Control> controls)
290  {
291    final StringBuilder b = getBuffer();
292    appendHeader(b, messageID);
293
294    b.append("     Bind Request Protocol Op:").append(StaticUtils.EOL);
295    b.append("          LDAP Version:  ").append(request.getVersion()).
296         append(StaticUtils.EOL);
297    b.append("          Bind DN:  ").append(request.getBindDN()).
298         append(StaticUtils.EOL);
299
300    switch (request.getCredentialsType())
301    {
302      case BindRequestProtocolOp.CRED_TYPE_SIMPLE:
303        b.append("          Credentials Type:  SIMPLE").append(StaticUtils.EOL);
304        b.append("               Password:  ").
305             append(request.getSimplePassword()).append(StaticUtils.EOL);
306        break;
307
308      case BindRequestProtocolOp.CRED_TYPE_SASL:
309        b.append("          Credentials Type:  SASL").append(StaticUtils.EOL);
310        b.append("               Mechanism:  ").
311             append(request.getSASLMechanism()).append(StaticUtils.EOL);
312
313        final ASN1OctetString saslCredentials = request.getSASLCredentials();
314        if (saslCredentials != null)
315        {
316          b.append("               Encoded Credentials:");
317          b.append(StaticUtils.EOL);
318          StaticUtils.toHexPlusASCII(saslCredentials.getValue(), 20, b);
319        }
320        break;
321    }
322
323    appendControls(b, controls);
324    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
325
326    final LDAPMessage responseMessage = requestHandler.processBindRequest(
327         messageID, request, controls);
328
329    b.setLength(0);
330    appendHeader(b, responseMessage.getMessageID());
331    b.append("     Bind Response Protocol Op:").append(StaticUtils.EOL);
332
333    final BindResponseProtocolOp protocolOp =
334         responseMessage.getBindResponseProtocolOp();
335    appendResponse(b, protocolOp.getResultCode(),
336         protocolOp.getDiagnosticMessage(),
337         protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
338
339    final ASN1OctetString serverSASLCredentials =
340         protocolOp.getServerSASLCredentials();
341    if (serverSASLCredentials != null)
342    {
343      b.append("               Encoded Server SASL Credentials:");
344      b.append(StaticUtils.EOL);
345      StaticUtils.toHexPlusASCII(serverSASLCredentials.getValue(), 20, b);
346    }
347
348    appendControls(b, responseMessage.getControls());
349    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
350
351    return responseMessage;
352  }
353
354
355
356  /**
357   * {@inheritDoc}
358   */
359  @Override()
360  public LDAPMessage processCompareRequest(final int messageID,
361                          final CompareRequestProtocolOp request,
362                          final List<Control> controls)
363  {
364    final StringBuilder b = getBuffer();
365    appendHeader(b, messageID);
366
367    b.append("     Compare Request Protocol Op:").append(StaticUtils.EOL);
368    b.append("          DN:  ").append(request.getDN()).append(StaticUtils.EOL);
369    b.append("          Attribute Type:  ").append(request.getAttributeName()).
370         append(StaticUtils.EOL);
371    b.append("          Assertion Value:  ").
372         append(request.getAssertionValue().stringValue()).
373         append(StaticUtils.EOL);
374
375    appendControls(b, controls);
376    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
377
378    final LDAPMessage responseMessage = requestHandler.processCompareRequest(
379         messageID, request, controls);
380
381    b.setLength(0);
382    appendHeader(b, responseMessage.getMessageID());
383    b.append("     Compare Response Protocol Op:").append(StaticUtils.EOL);
384
385    final CompareResponseProtocolOp protocolOp =
386         responseMessage.getCompareResponseProtocolOp();
387    appendResponse(b, protocolOp.getResultCode(),
388         protocolOp.getDiagnosticMessage(),
389         protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
390
391    appendControls(b, responseMessage.getControls());
392    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
393
394    return responseMessage;
395  }
396
397
398
399  /**
400   * {@inheritDoc}
401   */
402  @Override()
403  public LDAPMessage processDeleteRequest(final int messageID,
404                                          final DeleteRequestProtocolOp request,
405                                          final List<Control> controls)
406  {
407    final StringBuilder b = getBuffer();
408    appendHeader(b, messageID);
409
410    b.append("     Delete Request Protocol Op:").append(StaticUtils.EOL);
411    b.append("          DN:  ").append(request.getDN()).append(StaticUtils.EOL);
412
413    appendControls(b, controls);
414    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
415
416    final LDAPMessage responseMessage = requestHandler.processDeleteRequest(
417         messageID, request, controls);
418
419    b.setLength(0);
420    appendHeader(b, responseMessage.getMessageID());
421    b.append("     Delete Response Protocol Op:").append(StaticUtils.EOL);
422
423    final DeleteResponseProtocolOp protocolOp =
424         responseMessage.getDeleteResponseProtocolOp();
425    appendResponse(b, protocolOp.getResultCode(),
426         protocolOp.getDiagnosticMessage(),
427         protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
428
429    appendControls(b, responseMessage.getControls());
430    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
431
432    return responseMessage;
433  }
434
435
436
437  /**
438   * {@inheritDoc}
439   */
440  @Override()
441  public LDAPMessage processExtendedRequest(final int messageID,
442                          final ExtendedRequestProtocolOp request,
443                          final List<Control> controls)
444  {
445    final StringBuilder b = getBuffer();
446    appendHeader(b, messageID);
447
448    b.append("     Extended Request Protocol Op:").append(StaticUtils.EOL);
449    b.append("          Request OID:  ").append(request.getOID()).
450         append(StaticUtils.EOL);
451
452    final ASN1OctetString requestValue = request.getValue();
453    if (requestValue != null)
454    {
455      b.append("          Encoded Request Value:");
456      b.append(StaticUtils.EOL);
457      StaticUtils.toHexPlusASCII(requestValue.getValue(), 15, b);
458    }
459
460    appendControls(b, controls);
461    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
462
463    final LDAPMessage responseMessage = requestHandler.processExtendedRequest(
464         messageID, request, controls);
465
466    b.setLength(0);
467    appendHeader(b, responseMessage.getMessageID());
468    b.append("     Extended Response Protocol Op:").append(StaticUtils.EOL);
469
470    final ExtendedResponseProtocolOp protocolOp =
471         responseMessage.getExtendedResponseProtocolOp();
472    appendResponse(b, protocolOp.getResultCode(),
473         protocolOp.getDiagnosticMessage(),
474         protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
475
476    final String responseOID = protocolOp.getResponseOID();
477    if (responseOID != null)
478    {
479      b.append("          Response OID:  ").append(responseOID).
480           append(StaticUtils.EOL);
481    }
482
483    final ASN1OctetString responseValue = protocolOp.getResponseValue();
484    if (responseValue != null)
485    {
486      b.append("          Encoded Response Value:");
487      b.append(StaticUtils.EOL);
488      StaticUtils.toHexPlusASCII(responseValue.getValue(), 15, b);
489    }
490
491    appendControls(b, responseMessage.getControls());
492    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
493
494    return responseMessage;
495  }
496
497
498
499  /**
500   * {@inheritDoc}
501   */
502  @Override()
503  public LDAPMessage processModifyRequest(final int messageID,
504                                          final ModifyRequestProtocolOp request,
505                                          final List<Control> controls)
506  {
507    final StringBuilder b = getBuffer();
508    appendHeader(b, messageID);
509
510    b.append("     Modify Request Protocol Op:").append(StaticUtils.EOL);
511
512    final LDIFModifyChangeRecord changeRecord =
513         new LDIFModifyChangeRecord(request.getDN(),
514              request.getModifications());
515    final String[] ldifLines = changeRecord.toLDIF(80);
516    for (final String line : ldifLines)
517    {
518      b.append("          ").append(line).append(StaticUtils.EOL);
519    }
520
521    appendControls(b, controls);
522    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
523
524    final LDAPMessage responseMessage = requestHandler.processModifyRequest(
525         messageID, request, controls);
526
527    b.setLength(0);
528    appendHeader(b, responseMessage.getMessageID());
529    b.append("     Modify Response Protocol Op:").append(StaticUtils.EOL);
530
531    final ModifyResponseProtocolOp protocolOp =
532         responseMessage.getModifyResponseProtocolOp();
533    appendResponse(b, protocolOp.getResultCode(),
534         protocolOp.getDiagnosticMessage(),
535         protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
536
537    appendControls(b, responseMessage.getControls());
538    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
539
540    return responseMessage;
541  }
542
543
544
545  /**
546   * {@inheritDoc}
547   */
548  @Override()
549  public LDAPMessage processModifyDNRequest(final int messageID,
550                          final ModifyDNRequestProtocolOp request,
551                          final List<Control> controls)
552  {
553    final StringBuilder b = getBuffer();
554    appendHeader(b, messageID);
555
556    b.append("     Modify DN Request Protocol Op:").append(StaticUtils.EOL);
557    b.append("          DN:  ").append(request.getDN()).append(StaticUtils.EOL);
558    b.append("          New RDN:  ").append(request.getNewRDN()).
559         append(StaticUtils.EOL);
560    b.append("          Delete Old RDN:  ").append(request.deleteOldRDN()).
561         append(StaticUtils.EOL);
562
563    final String newSuperior = request.getNewSuperiorDN();
564    if (newSuperior != null)
565    {
566      b.append("          New Superior DN:  ").append(newSuperior).
567           append(StaticUtils.EOL);
568    }
569
570    appendControls(b, controls);
571    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
572
573    final LDAPMessage responseMessage = requestHandler.processModifyDNRequest(
574         messageID, request, controls);
575
576    b.setLength(0);
577    appendHeader(b, responseMessage.getMessageID());
578    b.append("     Modify DN Response Protocol Op:").append(StaticUtils.EOL);
579
580    final ModifyDNResponseProtocolOp protocolOp =
581         responseMessage.getModifyDNResponseProtocolOp();
582    appendResponse(b, protocolOp.getResultCode(),
583         protocolOp.getDiagnosticMessage(),
584         protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
585
586    appendControls(b, responseMessage.getControls());
587    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
588
589    return responseMessage;
590  }
591
592
593
594  /**
595   * {@inheritDoc}
596   */
597  @Override()
598  public LDAPMessage processSearchRequest(final int messageID,
599                                          final SearchRequestProtocolOp request,
600                                          final List<Control> controls)
601  {
602    final StringBuilder b = getBuffer();
603    appendHeader(b, messageID);
604
605    b.append("     Search Request Protocol Op:").append(StaticUtils.EOL);
606    b.append("          Base DN:  ").append(request.getBaseDN()).
607         append(StaticUtils.EOL);
608    b.append("          Scope:  ").append(request.getScope()).
609         append(StaticUtils.EOL);
610    b.append("          Dereference Policy:  ").
611         append(request.getDerefPolicy()).append(StaticUtils.EOL);
612    b.append("          Size Limit:  ").append(request.getSizeLimit()).
613         append(StaticUtils.EOL);
614    b.append("          Time Limit:  ").append(request.getSizeLimit()).
615         append(StaticUtils.EOL);
616    b.append("          Types Only:  ").append(request.typesOnly()).
617         append(StaticUtils.EOL);
618    b.append("          Filter:  ");
619    request.getFilter().toString(b);
620    b.append(StaticUtils.EOL);
621
622    final List<String> attributes = request.getAttributes();
623    if (! attributes.isEmpty())
624    {
625      b.append("          Requested Attributes:").append(StaticUtils.EOL);
626      for (final String attr : attributes)
627      {
628        b.append("               ").append(attr).append(StaticUtils.EOL);
629      }
630    }
631
632    appendControls(b, controls);
633    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
634
635    final LDAPMessage responseMessage = requestHandler.processSearchRequest(
636         messageID, request, controls);
637
638    b.setLength(0);
639    appendHeader(b, responseMessage.getMessageID());
640    b.append("     Search Result Done Protocol Op:").append(StaticUtils.EOL);
641
642    final SearchResultDoneProtocolOp protocolOp =
643         responseMessage.getSearchResultDoneProtocolOp();
644    appendResponse(b, protocolOp.getResultCode(),
645         protocolOp.getDiagnosticMessage(),
646         protocolOp.getMatchedDN(), protocolOp.getReferralURLs());
647
648    appendControls(b, responseMessage.getControls());
649    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
650
651    return responseMessage;
652  }
653
654
655
656  /**
657   * {@inheritDoc}
658   */
659  @Override()
660  public void processUnbindRequest(final int messageID,
661                                   final UnbindRequestProtocolOp request,
662                                   final List<Control> controls)
663  {
664    final StringBuilder b = getBuffer();
665    appendHeader(b, messageID);
666
667    b.append("     Unbind Request Protocol Op:").append(StaticUtils.EOL);
668
669    appendControls(b, controls);
670    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
671
672    requestHandler.processUnbindRequest(messageID, request, controls);
673  }
674
675
676
677  /**
678   * Retrieves a {@code StringBuilder} that may be used to generate a log
679   * message.
680   *
681   * @return  A {@code StringBuilder} containing the LDAP message header.
682   */
683  private static StringBuilder getBuffer()
684  {
685    StringBuilder b = BUFFERS.get();
686    if (b == null)
687    {
688      b = new StringBuilder();
689      BUFFERS.set(b);
690    }
691    else
692    {
693      b.setLength(0);
694    }
695
696    return b;
697  }
698
699
700
701  /**
702   * Appends an LDAP message header to the provided buffer.
703   *
704   * @param  b          The buffer to which to write the header.
705   * @param  messageID  The message ID for the LDAP message.
706   */
707  private void appendHeader(final StringBuilder b, final int messageID)
708  {
709    b.append(headerString);
710    b.append("LDAP Message:").append(StaticUtils.EOL);
711    b.append("     Message ID:  ").append(messageID).append(StaticUtils.EOL);
712  }
713
714
715
716  /**
717   * Appends information about an LDAP response to the given buffer.
718   *
719   * @param  b                  The buffer to which to append the information.
720   * @param  resultCode         The result code for the response.
721   * @param  diagnosticMessage  The diagnostic message for the response, if any.
722   * @param  matchedDN          The matched DN for the response, if any.
723   * @param  referralURLs       The referral URLs for the response, if any.
724   */
725  private static void appendResponse(final StringBuilder b,
726                                     final int resultCode,
727                                     final String diagnosticMessage,
728                                     final String matchedDN,
729                                     final List<String> referralURLs)
730  {
731    b.append("          Result Code:  ").append(ResultCode.valueOf(resultCode)).
732         append(StaticUtils.EOL);
733
734    if (diagnosticMessage != null)
735    {
736      b.append("          Diagnostic Message:  ").append(diagnosticMessage).
737           append(StaticUtils.EOL);
738    }
739
740    if (matchedDN != null)
741    {
742      b.append("          Matched DN:  ").append(matchedDN).
743           append(StaticUtils.EOL);
744    }
745
746    if (! referralURLs.isEmpty())
747    {
748      b.append("          Referral URLs:").append(StaticUtils.EOL);
749      for (final String url : referralURLs)
750      {
751        b.append("               ").append(url).append(StaticUtils.EOL);
752      }
753    }
754  }
755
756
757
758  /**
759   * Appends information about the provided set of controls to the given buffer.
760   * A trailing EOL will also be appended.
761   *
762   * @param  b         The buffer to which to append the control information.
763   * @param  controls  The set of controls to be appended to the buffer.
764   */
765  private static void appendControls(final StringBuilder b,
766                                     final List<Control> controls)
767  {
768    if (! controls.isEmpty())
769    {
770      b.append("     Controls:").append(StaticUtils.EOL);
771
772      int index = 1;
773      for (final Control c : controls)
774      {
775        b.append("          Control ");
776        b.append(index++);
777        b.append(StaticUtils.EOL);
778        b.append("               OID:  ");
779        b.append(c.getOID());
780        b.append(StaticUtils.EOL);
781        b.append("               Is Critical:  ");
782        b.append(c.isCritical());
783        b.append(StaticUtils.EOL);
784
785        final ASN1OctetString value = c.getValue();
786        if ((value != null) && (value.getValueLength() > 0))
787        {
788          b.append("               Encoded Value:");
789          b.append(StaticUtils.EOL);
790          StaticUtils.toHexPlusASCII(value.getValue(), 20, b);
791        }
792
793        // If it is a subclass of Control rather than just a generic one, then
794        // it might have a useful toString representation, so provide it.
795        if (! c.getClass().getName().equals(Control.class.getName()))
796        {
797          b.append("               String Representation:  ");
798          c.toString(b);
799          b.append(StaticUtils.EOL);
800        }
801      }
802    }
803  }
804
805
806
807  /**
808   * Appends information about the provided set of controls to the given buffer.
809   *
810   * @param  b         The buffer to which to append the control information.
811   * @param  controls  The set of controls to be appended to the buffer.
812   */
813  private static void appendControls(final StringBuilder b,
814                                     final Control[] controls)
815  {
816    appendControls(b, Arrays.asList(controls));
817  }
818
819
820
821  /**
822   * {@inheritDoc}
823   */
824  @Override()
825  public ObjectPair<IntermediateResponseProtocolOp,Control[]>
826              transformIntermediateResponse(final int messageID,
827                   final IntermediateResponseProtocolOp response,
828                   final Control[] controls)
829  {
830    final StringBuilder b = getBuffer();
831    appendHeader(b, messageID);
832
833    b.append("     Intermediate Response Protocol Op:").append(StaticUtils.EOL);
834
835    final String oid = response.getOID();
836    if (oid != null)
837    {
838      b.append("          OID:  ").append(oid).append(StaticUtils.EOL);
839    }
840
841    final ASN1OctetString value = response.getValue();
842    if (value != null)
843    {
844      b.append("          Encoded Value:");
845      b.append(StaticUtils.EOL);
846      StaticUtils.toHexPlusASCII(value.getValue(), 15, b);
847    }
848
849    appendControls(b, controls);
850    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
851
852    return new ObjectPair<>(response, controls);
853  }
854
855
856
857  /**
858   * {@inheritDoc}
859   */
860  @Override()
861  public ObjectPair<SearchResultEntryProtocolOp,Control[]> transformEntry(
862              final int messageID, final SearchResultEntryProtocolOp entry,
863              final Control[] controls)
864  {
865    final StringBuilder b = getBuffer();
866    appendHeader(b, messageID);
867
868    b.append("     Search Result Entry Protocol Op:").append(StaticUtils.EOL);
869
870    final Entry e = new Entry(entry.getDN(), entry.getAttributes());
871    final String[] ldifLines = e.toLDIF(80);
872    for (final String line : ldifLines)
873    {
874      b.append("          ").append(line).append(StaticUtils.EOL);
875    }
876
877    appendControls(b, controls);
878    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
879
880    return new ObjectPair<>(entry, controls);
881  }
882
883
884
885  /**
886   * {@inheritDoc}
887   */
888  @Override()
889  public ObjectPair<SearchResultReferenceProtocolOp,Control[]>
890              transformReference(final int messageID,
891                   final SearchResultReferenceProtocolOp reference,
892                   final Control[] controls)
893  {
894    final StringBuilder b = getBuffer();
895    appendHeader(b, messageID);
896
897    b.append("     Search Result Reference Protocol Op:").
898         append(StaticUtils.EOL);
899    b.append("          Referral URLs:").append(StaticUtils.EOL);
900
901    for (final String url : reference.getReferralURLs())
902    {
903      b.append("               ").append(url).append(StaticUtils.EOL);
904    }
905
906    appendControls(b, controls);
907    logHandler.publish(new LogRecord(Level.INFO, b.toString()));
908
909    return new ObjectPair<>(reference, controls);
910  }
911}