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 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.tasks;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.List;
033    
034    import org.opends.server.admin.std.server.ConnectionHandlerCfg;
035    import org.opends.server.backends.task.Task;
036    import org.opends.server.backends.task.TaskState;
037    import org.opends.server.api.ClientConnection;
038    import org.opends.server.api.ConnectionHandler;
039    import org.opends.server.core.DirectoryServer;
040    import org.opends.server.types.Attribute;
041    import org.opends.server.types.AttributeType;
042    import org.opends.server.types.AttributeValue;
043    import org.opends.server.types.DirectoryException;
044    import org.opends.server.types.DisconnectReason;
045    import org.opends.server.types.Entry;
046    import org.opends.server.types.Operation;
047    import org.opends.server.types.Privilege;
048    import org.opends.server.types.ResultCode;
049    
050    import static org.opends.messages.TaskMessages.*;
051    import static org.opends.server.util.ServerConstants.*;
052    import static org.opends.server.util.StaticUtils.*;
053    
054    
055    
056    /**
057     * This class provides an implementation of a Directory Server task that can be
058     * used to terminate a client connection.
059     */
060    public class DisconnectClientTask
061           extends Task
062    {
063      // Indicates whether to send a notification message to the client.
064      private boolean notifyClient;
065    
066      // The connection ID for the client connection to terminate.
067      private long connectionID;
068    
069      // The disconnect message to send to the client.
070      private Message disconnectMessage;
071    
072      /**
073       * {@inheritDoc}
074       */
075      public Message getDisplayName() {
076        return INFO_TASK_DISCONNECT_CLIENT_NAME.get();
077      }
078    
079      /**
080       * {@inheritDoc}
081       */
082      @Override
083      public void initializeTask()
084             throws DirectoryException
085      {
086        // If the client connection is available, then make sure the client has the
087        // DISCONNECT_CLIENT privilege.
088        Operation operation = getOperation();
089        if (operation != null)
090        {
091          ClientConnection conn = operation.getClientConnection();
092          if (! conn.hasPrivilege(Privilege.DISCONNECT_CLIENT, operation))
093          {
094            Message message = ERR_TASK_DISCONNECT_NO_PRIVILEGE.get();
095            throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
096                                         message);
097          }
098        }
099    
100    
101        // Get the connection ID for the client connection.
102        Entry taskEntry = getTaskEntry();
103        connectionID = -1L;
104        AttributeType attrType =
105             DirectoryServer.getAttributeType(ATTR_TASK_DISCONNECT_CONN_ID, true);
106        List<Attribute> attrList = taskEntry.getAttribute(attrType);
107        if (attrList != null)
108        {
109    connIDLoop:
110          for (Attribute a : attrList)
111          {
112            for (AttributeValue v : a.getValues())
113            {
114              try
115              {
116                connectionID = Long.parseLong(v.getStringValue());
117                break connIDLoop;
118              }
119              catch (Exception e)
120              {
121                Message message =
122                    ERR_TASK_DISCONNECT_INVALID_CONN_ID.get(v.getStringValue());
123                throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
124                                             message, e);
125              }
126            }
127          }
128        }
129    
130        if (connectionID < 0)
131        {
132          Message message =
133              ERR_TASK_DISCONNECT_NO_CONN_ID.get(ATTR_TASK_DISCONNECT_CONN_ID);
134          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
135                                       message);
136        }
137    
138    
139        // Determine whether to notify the client.
140        notifyClient = false;
141        attrType =
142             DirectoryServer.getAttributeType(ATTR_TASK_DISCONNECT_NOTIFY_CLIENT,
143                                              true);
144        attrList = taskEntry.getAttribute(attrType);
145        if (attrList != null)
146        {
147    notifyClientLoop:
148          for (Attribute a : attrList)
149          {
150            for (AttributeValue v : a.getValues())
151            {
152              String stringValue = toLowerCase(v.getStringValue());
153              if (stringValue.equals("true"))
154              {
155                notifyClient = true;
156                break notifyClientLoop;
157              }
158              else if (stringValue.equals("false"))
159              {
160                break notifyClientLoop;
161              }
162              else
163              {
164                Message message =
165                    ERR_TASK_DISCONNECT_INVALID_NOTIFY_CLIENT.get(stringValue);
166                throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
167                                             message);
168              }
169            }
170          }
171        }
172    
173    
174        // Get the disconnect message.
175        disconnectMessage = INFO_TASK_DISCONNECT_GENERIC_MESSAGE.get();
176        attrType = DirectoryServer.getAttributeType(ATTR_TASK_DISCONNECT_MESSAGE,
177                                                    true);
178        attrList = taskEntry.getAttribute(attrType);
179        if (attrList != null)
180        {
181    disconnectMessageLoop:
182          for (Attribute a : attrList)
183          {
184            for (AttributeValue v : a.getValues())
185            {
186              disconnectMessage = Message.raw(v.getStringValue());
187              break disconnectMessageLoop;
188            }
189          }
190        }
191      }
192    
193    
194    
195      /**
196       * {@inheritDoc}
197       */
198      protected TaskState runTask()
199      {
200        // Get the specified client connection.
201        ClientConnection clientConnection = null;
202        for (ConnectionHandler handler : DirectoryServer.getConnectionHandlers())
203        {
204          ConnectionHandler<? extends ConnectionHandlerCfg> connHandler =
205               (ConnectionHandler<? extends ConnectionHandlerCfg>) handler;
206          for (ClientConnection c : connHandler.getClientConnections())
207          {
208            if (c.getConnectionID() == connectionID)
209            {
210              clientConnection = c;
211              break;
212            }
213          }
214        }
215    
216    
217        // If there is no such client connection, then return an error.  Otherwise,
218        // terminate it.
219        if (clientConnection == null)
220        {
221          Message message =
222              ERR_TASK_DISCONNECT_NO_SUCH_CONNECTION.get(
223                      String.valueOf(connectionID));
224          logError(message);
225    
226          return TaskState.COMPLETED_WITH_ERRORS;
227        }
228        else
229        {
230          clientConnection.disconnect(DisconnectReason.ADMIN_DISCONNECT,
231                                      notifyClient, disconnectMessage);
232          return TaskState.COMPLETED_SUCCESSFULLY;
233        }
234      }
235    }
236