/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.tasks;
import org.opends.messages.Message;



import java.util.List;

import org.opends.server.backends.task.Task;
import org.opends.server.backends.task.TaskState;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConnectionHandler;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.Entry;
import org.opends.server.types.Operation;
import org.opends.server.types.Privilege;
import org.opends.server.types.ResultCode;

import static org.opends.messages.TaskMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;



/**
 * This class provides an implementation of a Directory Server task that can be
 * used to terminate a client connection.
 */
public class DisconnectClientTask
       extends Task
{
  // Indicates whether to send a notification message to the client.
  private boolean notifyClient;

  // The connection ID for the client connection to terminate.
  private long connectionID;

  // The disconnect message to send to the client.
  private Message disconnectMessage;

  /**
   * {@inheritDoc}
   */
  @Override
  public Message getDisplayName() {
    return INFO_TASK_DISCONNECT_CLIENT_NAME.get();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void initializeTask()
         throws DirectoryException
  {
    // If the client connection is available, then make sure the client has the
    // DISCONNECT_CLIENT privilege.
    Operation operation = getOperation();
    if (operation != null)
    {
      ClientConnection conn = operation.getClientConnection();
      if (! conn.hasPrivilege(Privilege.DISCONNECT_CLIENT, operation))
      {
        Message message = ERR_TASK_DISCONNECT_NO_PRIVILEGE.get();
        throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
                                     message);
      }
    }


    // Get the connection ID for the client connection.
    Entry taskEntry = getTaskEntry();
    connectionID = -1L;
    AttributeType attrType =
         DirectoryServer.getAttributeType(ATTR_TASK_DISCONNECT_CONN_ID, true);
    List<Attribute> attrList = taskEntry.getAttribute(attrType);
    if (attrList != null)
    {
connIDLoop:
      for (Attribute a : attrList)
      {
        for (AttributeValue v : a)
        {
          try
          {
            connectionID = Long.parseLong(v.getValue().toString());
            break connIDLoop;
          }
          catch (Exception e)
          {
            Message message =
               ERR_TASK_DISCONNECT_INVALID_CONN_ID.get(v.getValue().toString());
            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                         message, e);
          }
        }
      }
    }

    if (connectionID < 0)
    {
      Message message =
          ERR_TASK_DISCONNECT_NO_CONN_ID.get(ATTR_TASK_DISCONNECT_CONN_ID);
      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
                                   message);
    }


    // Determine whether to notify the client.
    notifyClient = false;
    attrType =
         DirectoryServer.getAttributeType(ATTR_TASK_DISCONNECT_NOTIFY_CLIENT,
                                          true);
    attrList = taskEntry.getAttribute(attrType);
    if (attrList != null)
    {
notifyClientLoop:
      for (Attribute a : attrList)
      {
        for (AttributeValue v : a)
        {
          String stringValue = toLowerCase(v.getValue().toString());
          if (stringValue.equals("true"))
          {
            notifyClient = true;
            break notifyClientLoop;
          }
          else if (stringValue.equals("false"))
          {
            break notifyClientLoop;
          }
          else
          {
            Message message =
                ERR_TASK_DISCONNECT_INVALID_NOTIFY_CLIENT.get(stringValue);
            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                         message);
          }
        }
      }
    }


    // Get the disconnect message.
    disconnectMessage = INFO_TASK_DISCONNECT_GENERIC_MESSAGE.get();
    attrType = DirectoryServer.getAttributeType(ATTR_TASK_DISCONNECT_MESSAGE,
                                                true);
    attrList = taskEntry.getAttribute(attrType);
    if (attrList != null)
    {
disconnectMessageLoop:
      for (Attribute a : attrList)
      {
        for (AttributeValue v : a)
        {
          disconnectMessage = Message.raw(v.getValue().toString());
          break disconnectMessageLoop;
        }
      }
    }
  }



  /**
   * {@inheritDoc}
   */
  @Override
  protected TaskState runTask()
  {
    // Get the specified client connection.
    ClientConnection clientConnection = null;
    for (ConnectionHandler<?> handler : DirectoryServer.getConnectionHandlers())
    {
      for (ClientConnection c : handler.getClientConnections())
      {
        if (c.getConnectionID() == connectionID)
        {
          clientConnection = c;
          break;
        }
      }
    }


    // If there is no such client connection, then return an error.  Otherwise,
    // terminate it.
    if (clientConnection == null)
    {
      Message message =
          ERR_TASK_DISCONNECT_NO_SUCH_CONNECTION.get(
                  String.valueOf(connectionID));
      logError(message);

      return TaskState.COMPLETED_WITH_ERRORS;
    }
    else
    {
      clientConnection.disconnect(DisconnectReason.ADMIN_DISCONNECT,
                                  notifyClient, disconnectMessage);
      return TaskState.COMPLETED_SUCCESSFULLY;
    }
  }
}

