Esempio n. 1
0
  /**
   * Process the AdHoc-Command packet that request the execution of some action of a command. If
   * this is the first request, this method checks, before executing the command, if:
   *
   * <ul>
   *   <li>The requested command exists
   *   <li>The requester has permissions to execute it
   *   <li>The command has more than one stage, if so, it saves the command and session ID for
   *       further use
   * </ul>
   *
   * <br>
   * <br>
   * If this is not the first request, this method checks, before executing the command, if:
   *
   * <ul>
   *   <li>The session ID of the request was stored
   *   <li>The session life do not exceed the time out
   *   <li>The action to execute is one of the available actions
   * </ul>
   *
   * @param requestData the packet to process.
   */
  private void processAdHocCommand(AdHocCommandData requestData) {
    // Only process requests of type SET
    if (requestData.getType() != IQ.Type.SET) {
      return;
    }

    // Creates the response with the corresponding data
    AdHocCommandData response = new AdHocCommandData();
    response.setTo(requestData.getFrom());
    response.setPacketID(requestData.getPacketID());
    response.setNode(requestData.getNode());
    response.setId(requestData.getTo());

    String sessionId = requestData.getSessionID();
    String commandNode = requestData.getNode();

    if (sessionId == null) {
      // A new execution request has been received. Check that the
      // command exists
      if (!commands.containsKey(commandNode)) {
        // Requested command does not exist so return
        // item_not_found error.
        respondError(response, XMPPError.Condition.item_not_found);
        return;
      }

      // Create new session ID
      sessionId = StringUtils.randomString(15);

      try {
        // Create a new instance of the command with the
        // corresponding sessioid
        LocalCommand command = newInstanceOfCmd(commandNode, sessionId);

        response.setType(IQ.Type.RESULT);
        command.setData(response);

        // Check that the requester has enough permission.
        // Answer forbidden error if requester permissions are not
        // enough to execute the requested command
        if (!command.hasPermission(requestData.getFrom())) {
          respondError(response, XMPPError.Condition.forbidden);
          return;
        }

        Action action = requestData.getAction();

        // If the action is unknown then respond an error.
        if (action != null && action.equals(Action.unknown)) {
          respondError(
              response,
              XMPPError.Condition.bad_request,
              AdHocCommand.SpecificErrorCondition.malformedAction);
          return;
        }

        // If the action is not execute, then it is an invalid action.
        if (action != null && !action.equals(Action.execute)) {
          respondError(
              response,
              XMPPError.Condition.bad_request,
              AdHocCommand.SpecificErrorCondition.badAction);
          return;
        }

        // Increase the state number, so the command knows in witch
        // stage it is
        command.incrementStage();
        // Executes the command
        command.execute();

        if (command.isLastStage()) {
          // If there is only one stage then the command is completed
          response.setStatus(Status.completed);
        } else {
          // Else it is still executing, and is registered to be
          // available for the next call
          response.setStatus(Status.executing);
          executingCommands.put(sessionId, command);
          // See if the session reaping thread is started. If not, start it.
          if (!sessionsSweeper.isAlive()) {
            sessionsSweeper.start();
          }
        }

        // Sends the response packet
        connection.sendPacket(response);

      } catch (XMPPException e) {
        // If there is an exception caused by the next, complete,
        // prev or cancel method, then that error is returned to the
        // requester.
        XMPPError error = e.getXMPPError();

        // If the error type is cancel, then the execution is
        // canceled therefore the status must show that, and the
        // command be removed from the executing list.
        if (XMPPError.Type.CANCEL.equals(error.getType())) {
          response.setStatus(Status.canceled);
          executingCommands.remove(sessionId);
        }
        respondError(response, error);
        e.printStackTrace();
      }
    } else {
      LocalCommand command = executingCommands.get(sessionId);

      // Check that a command exists for the specified sessionID
      // This also handles if the command was removed in the meanwhile
      // of getting the key and the value of the map.
      if (command == null) {
        respondError(
            response,
            XMPPError.Condition.bad_request,
            AdHocCommand.SpecificErrorCondition.badSessionid);
        return;
      }

      // Check if the Session data has expired (default is 10 minutes)
      long creationStamp = command.getCreationDate();
      if (System.currentTimeMillis() - creationStamp > SESSION_TIMEOUT * 1000) {
        // Remove the expired session
        executingCommands.remove(sessionId);

        // Answer a not_allowed error (session-expired)
        respondError(
            response,
            XMPPError.Condition.not_allowed,
            AdHocCommand.SpecificErrorCondition.sessionExpired);
        return;
      }

      /*
       * Since the requester could send two requests for the same
       * executing command i.e. the same session id, all the execution of
       * the action must be synchronized to avoid inconsistencies.
       */
      synchronized (command) {
        Action action = requestData.getAction();

        // If the action is unknown the respond an error
        if (action != null && action.equals(Action.unknown)) {
          respondError(
              response,
              XMPPError.Condition.bad_request,
              AdHocCommand.SpecificErrorCondition.malformedAction);
          return;
        }

        // If the user didn't specify an action or specify the execute
        // action then follow the actual default execute action
        if (action == null || Action.execute.equals(action)) {
          action = command.getExecuteAction();
        }

        // Check that the specified action was previously
        // offered
        if (!command.isValidAction(action)) {
          respondError(
              response,
              XMPPError.Condition.bad_request,
              AdHocCommand.SpecificErrorCondition.badAction);
          return;
        }

        try {
          // TODO: Check that all the requierd fields of the form are
          // TODO: filled, if not throw an exception. This will simplify the
          // TODO: construction of new commands

          // Since all errors were passed, the response is now a
          // result
          response.setType(IQ.Type.RESULT);

          // Set the new data to the command.
          command.setData(response);

          if (Action.next.equals(action)) {
            command.incrementStage();
            command.next(new Form(requestData.getForm()));
            if (command.isLastStage()) {
              // If it is the last stage then the command is
              // completed
              response.setStatus(Status.completed);
            } else {
              // Otherwise it is still executing
              response.setStatus(Status.executing);
            }
          } else if (Action.complete.equals(action)) {
            command.incrementStage();
            command.complete(new Form(requestData.getForm()));
            response.setStatus(Status.completed);
            // Remove the completed session
            executingCommands.remove(sessionId);
          } else if (Action.prev.equals(action)) {
            command.decrementStage();
            command.prev();
          } else if (Action.cancel.equals(action)) {
            command.cancel();
            response.setStatus(Status.canceled);
            // Remove the canceled session
            executingCommands.remove(sessionId);
          }

          connection.sendPacket(response);
        } catch (XMPPException e) {
          // If there is an exception caused by the next, complete,
          // prev or cancel method, then that error is returned to the
          // requester.
          XMPPError error = e.getXMPPError();

          // If the error type is cancel, then the execution is
          // canceled therefore the status must show that, and the
          // command be removed from the executing list.
          if (XMPPError.Type.CANCEL.equals(error.getType())) {
            response.setStatus(Status.canceled);
            executingCommands.remove(sessionId);
          }
          respondError(response, error);

          e.printStackTrace();
        }
      }
    }
  }