/**
   * Sends enable or disable carbon packet to the server.
   *
   * @param enable if <tt>true</tt> sends enable packet otherwise sends disable packet.
   */
  private void enableDisableCarbon(final boolean enable) {
    IQ iq =
        new IQ() {

          @Override
          public String getChildElementXML() {
            return "<" + (enable ? "enable" : "disable") + " xmlns='urn:xmpp:carbons:2' />";
          }
        };

    Packet response = null;
    try {
      PacketCollector packetCollector =
          jabberProvider
              .getConnection()
              .createPacketCollector(new PacketIDFilter(iq.getPacketID()));
      iq.setFrom(jabberProvider.getOurJID());
      iq.setType(IQ.Type.SET);
      jabberProvider.getConnection().sendPacket(iq);
      response = packetCollector.nextResult(SmackConfiguration.getPacketReplyTimeout());

      packetCollector.cancel();
    } catch (Exception e) {
      logger.error("Failed to enable carbon.", e);
    }

    isCarbonEnabled = false;

    if (response == null) {
      logger.error("Failed to enable carbon. No response is received.");
    } else if (response.getError() != null) {
      logger.error("Failed to enable carbon: " + response.getError());
    } else if (!(response instanceof IQ) || !((IQ) response).getType().equals(IQ.Type.RESULT)) {
      logger.error("Failed to enable carbon. The response is not correct.");
    } else {
      isCarbonEnabled = true;
    }
  }
  /**
   * Implements {@link PacketListener}. Notifies this instance that a specific {@link Packet} (which
   * this instance has already expressed interest into by returning <tt>true</tt> from {@link
   * #accept(Packet)}) has been received.
   *
   * @param packet the <tt>Packet</tt> which has been received and which this instance is given a
   *     chance to process
   */
  public void processPacket(Packet packet) {
    /*
     * As we do elsewhere, acknowledge the receipt of the Packet first and
     * then go about our business with it.
     */
    IQ iq = (IQ) packet;

    if (iq.getType() == IQ.Type.SET)
      protocolProvider.getConnection().sendPacket(IQ.createResultIQ(iq));

    /*
     * Now that the acknowledging is out of the way, do go about our
     * business with the Packet.
     */
    ColibriConferenceIQ conferenceIQ = (ColibriConferenceIQ) iq;
    boolean interrupted = false;

    try {
      processColibriConferenceIQ(conferenceIQ);
    } catch (Throwable t) {
      logger.error(
          "An error occurred during the processing of a " + packet.getClass().getName() + " packet",
          t);

      if (t instanceof InterruptedException) {
        /*
         * We cleared the interrupted state of the current Thread by
         * catching the InterruptedException. However, we do not really
         * care whether the current Thread has been interrupted - we
         * caught the InterruptedException because we want to swallow
         * any Throwable. Consequently, we should better restore the
         * interrupted state.
         */
        interrupted = true;
      } else if (t instanceof ThreadDeath) throw (ThreadDeath) t;
    }
    if (interrupted) Thread.currentThread().interrupt();
  }
    /**
     * Handles incoming messages and dispatches whatever events are necessary.
     *
     * @param packet the packet that we need to handle (if it is a message).
     */
    public void processPacket(Packet packet) {
      if (!(packet instanceof org.jivesoftware.smack.packet.Message)) return;

      org.jivesoftware.smack.packet.Message msg = (org.jivesoftware.smack.packet.Message) packet;

      boolean isForwardedSentMessage = false;
      if (msg.getBody() == null) {

        CarbonPacketExtension carbonExt =
            (CarbonPacketExtension) msg.getExtension(CarbonPacketExtension.NAMESPACE);
        if (carbonExt == null) return;

        isForwardedSentMessage =
            (carbonExt.getElementName() == CarbonPacketExtension.SENT_ELEMENT_NAME);
        List<ForwardedPacketExtension> extensions =
            carbonExt.getChildExtensionsOfType(ForwardedPacketExtension.class);
        if (extensions.isEmpty()) return;
        ForwardedPacketExtension forwardedExt = extensions.get(0);
        msg = forwardedExt.getMessage();
        if (msg == null || msg.getBody() == null) return;
      }

      Object multiChatExtension = msg.getExtension("x", "http://jabber.org/protocol/muc#user");

      // its not for us
      if (multiChatExtension != null) return;

      String userFullId = isForwardedSentMessage ? msg.getTo() : msg.getFrom();

      String userBareID = StringUtils.parseBareAddress(userFullId);

      boolean isPrivateMessaging = false;
      ChatRoom privateContactRoom = null;
      OperationSetMultiUserChatJabberImpl mucOpSet =
          (OperationSetMultiUserChatJabberImpl)
              jabberProvider.getOperationSet(OperationSetMultiUserChat.class);
      if (mucOpSet != null) privateContactRoom = mucOpSet.getChatRoom(userBareID);

      if (privateContactRoom != null) {
        isPrivateMessaging = true;
      }

      if (logger.isDebugEnabled()) {
        if (logger.isDebugEnabled())
          logger.debug("Received from " + userBareID + " the message " + msg.toXML());
      }

      Message newMessage = createMessage(msg.getBody(), DEFAULT_MIME_TYPE, msg.getPacketID());

      // check if the message is available in xhtml
      PacketExtension ext = msg.getExtension("http://jabber.org/protocol/xhtml-im");

      if (ext != null) {
        XHTMLExtension xhtmlExt = (XHTMLExtension) ext;

        // parse all bodies
        Iterator<String> bodies = xhtmlExt.getBodies();
        StringBuffer messageBuff = new StringBuffer();
        while (bodies.hasNext()) {
          String body = bodies.next();
          messageBuff.append(body);
        }

        if (messageBuff.length() > 0) {
          // we remove body tags around message cause their
          // end body tag is breaking
          // the visualization as html in the UI
          String receivedMessage =
              messageBuff
                  .toString()
                  // removes body start tag
                  .replaceAll("\\<[bB][oO][dD][yY].*?>", "")
                  // removes body end tag
                  .replaceAll("\\</[bB][oO][dD][yY].*?>", "");

          // for some reason &apos; is not rendered correctly
          // from our ui, lets use its equivalent. Other
          // similar chars(< > & ") seem ok.
          receivedMessage = receivedMessage.replaceAll("&apos;", "&#39;");

          newMessage = createMessage(receivedMessage, HTML_MIME_TYPE, msg.getPacketID());
        }
      }

      PacketExtension correctionExtension = msg.getExtension(MessageCorrectionExtension.NAMESPACE);
      String correctedMessageUID = null;
      if (correctionExtension != null) {
        correctedMessageUID =
            ((MessageCorrectionExtension) correctionExtension).getCorrectedMessageUID();
      }

      Contact sourceContact =
          opSetPersPresence.findContactByID((isPrivateMessaging ? userFullId : userBareID));
      if (msg.getType() == org.jivesoftware.smack.packet.Message.Type.error) {
        // error which is multichat and we don't know about the contact
        // is a muc message error which is missing muc extension
        // and is coming from the room, when we try to send message to
        // room which was deleted or offline on the server
        if (isPrivateMessaging && sourceContact == null) {
          if (privateContactRoom != null) {
            XMPPError error = packet.getError();
            int errorResultCode = ChatRoomMessageDeliveryFailedEvent.UNKNOWN_ERROR;

            if (error != null && error.getCode() == 403) {
              errorResultCode = ChatRoomMessageDeliveryFailedEvent.FORBIDDEN;
            }

            String errorReason = error.getMessage();

            ChatRoomMessageDeliveryFailedEvent evt =
                new ChatRoomMessageDeliveryFailedEvent(
                    privateContactRoom, null, errorResultCode, errorReason, new Date(), newMessage);
            ((ChatRoomJabberImpl) privateContactRoom).fireMessageEvent(evt);
          }

          return;
        }

        if (logger.isInfoEnabled()) logger.info("Message error received from " + userBareID);

        int errorResultCode = MessageDeliveryFailedEvent.UNKNOWN_ERROR;
        if (packet.getError() != null) {
          int errorCode = packet.getError().getCode();

          if (errorCode == 503) {
            org.jivesoftware.smackx.packet.MessageEvent msgEvent =
                (org.jivesoftware.smackx.packet.MessageEvent)
                    packet.getExtension("x", "jabber:x:event");
            if (msgEvent != null && msgEvent.isOffline()) {
              errorResultCode = MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED;
            }
          }
        }

        if (sourceContact == null) {
          sourceContact = opSetPersPresence.createVolatileContact(userFullId, isPrivateMessaging);
        }

        MessageDeliveryFailedEvent ev =
            new MessageDeliveryFailedEvent(
                newMessage, sourceContact, correctedMessageUID, errorResultCode);

        // ev = messageDeliveryFailedTransform(ev);

        if (ev != null) fireMessageEvent(ev);
        return;
      }
      putJidForAddress(userFullId, msg.getThread());

      // In the second condition we filter all group chat messages,
      // because they are managed by the multi user chat operation set.
      if (sourceContact == null) {
        if (logger.isDebugEnabled())
          logger.debug("received a message from an unknown contact: " + userBareID);
        // create the volatile contact
        sourceContact = opSetPersPresence.createVolatileContact(userFullId, isPrivateMessaging);
      }

      Date timestamp = new Date();
      // Check for XEP-0091 timestamp (deprecated)
      PacketExtension delay = msg.getExtension("x", "jabber:x:delay");
      if (delay != null && delay instanceof DelayInformation) {
        timestamp = ((DelayInformation) delay).getStamp();
      }
      // check for XEP-0203 timestamp
      delay = msg.getExtension("delay", "urn:xmpp:delay");
      if (delay != null && delay instanceof DelayInfo) {
        timestamp = ((DelayInfo) delay).getStamp();
      }

      ContactResource resource = ((ContactJabberImpl) sourceContact).getResourceFromJid(userFullId);

      EventObject msgEvt = null;
      if (!isForwardedSentMessage)
        msgEvt =
            new MessageReceivedEvent(
                newMessage,
                sourceContact,
                resource,
                timestamp,
                correctedMessageUID,
                isPrivateMessaging,
                privateContactRoom);
      else msgEvt = new MessageDeliveredEvent(newMessage, sourceContact, timestamp);
      // msgReceivedEvt = messageReceivedTransform(msgReceivedEvt);
      if (msgEvt != null) fireMessageEvent(msgEvt);
    }