/**
     * Process a request from a distant contact
     *
     * @param requestEvent the <tt>RequestEvent</tt> containing the newly received request.
     * @return <tt>true</tt> if the specified event has been handled by this processor and shouldn't
     *     be offered to other processors registered for the same method; <tt>false</tt>, otherwise
     */
    @Override
    public boolean processRequest(RequestEvent requestEvent) {
      synchronized (messageProcessors) {
        for (SipMessageProcessor listener : messageProcessors)
          if (!listener.processMessage(requestEvent)) return true;
      }

      // get the content
      String content = null;
      Request req = requestEvent.getRequest();
      try {

        content = new String(req.getRawContent(), getCharset(req));
      } catch (UnsupportedEncodingException ex) {
        if (logger.isDebugEnabled()) logger.debug("failed to convert the message charset");
        content = new String(requestEvent.getRequest().getRawContent());
      }

      // who sent this request ?
      FromHeader fromHeader = (FromHeader) requestEvent.getRequest().getHeader(FromHeader.NAME);

      if (fromHeader == null) {
        logger.error("received a request without a from header");
        return false;
      }

      Contact from =
          opSetPersPresence.resolveContactID(fromHeader.getAddress().getURI().toString());

      ContentTypeHeader ctheader = (ContentTypeHeader) req.getHeader(ContentTypeHeader.NAME);

      String ctype = null;
      String cencoding = null;

      if (ctheader == null) {
        ctype = DEFAULT_MIME_TYPE;
      } else {
        ctype = ctheader.getContentType() + "/" + ctheader.getContentSubType();
        cencoding = ctheader.getParameter("charset");
      }

      if (cencoding == null) cencoding = DEFAULT_MIME_ENCODING;

      Message newMessage = createMessage(content, ctype, cencoding, null);

      if (from == null) {
        if (logger.isDebugEnabled())
          logger.debug(
              "received a message from an unknown contact: "
                  + fromHeader.getAddress().getURI().toString());
        // create the volatile contact
        from = opSetPersPresence.createVolatileContact(fromHeader.getAddress().getURI().toString());
      }

      // answer ok
      try {
        Response ok =
            sipProvider.getMessageFactory().createResponse(Response.OK, requestEvent.getRequest());
        SipStackSharing.getOrCreateServerTransaction(requestEvent).sendResponse(ok);
      } catch (ParseException exc) {
        logger.error("failed to build the response", exc);
      } catch (SipException exc) {
        logger.error("failed to send the response : " + exc.getMessage(), exc);
      } catch (InvalidArgumentException exc) {
        if (logger.isDebugEnabled())
          logger.debug("Invalid argument for createResponse : " + exc.getMessage(), exc);
      }

      // fire an event
      MessageReceivedEvent msgReceivedEvt =
          new MessageReceivedEvent(newMessage, from, System.currentTimeMillis());
      fireMessageEvent(msgReceivedEvt);

      return true;
    }
    /**
     * Process a response from a distant contact.
     *
     * @param responseEvent the <tt>ResponseEvent</tt> containing the newly received SIP response.
     * @return <tt>true</tt> if the specified event has been handled by this processor and shouldn't
     *     be offered to other processors registered for the same method; <tt>false</tt>, otherwise
     */
    @Override
    public boolean processResponse(ResponseEvent responseEvent) {
      synchronized (messageProcessors) {
        for (SipMessageProcessor listener : messageProcessors)
          if (!listener.processResponse(responseEvent, sentMsg)) return true;
      }

      Request req = responseEvent.getClientTransaction().getRequest();
      int status = responseEvent.getResponse().getStatusCode();
      // content of the response
      String content = null;

      try {
        content = new String(req.getRawContent(), getCharset(req));
      } catch (UnsupportedEncodingException exc) {
        if (logger.isDebugEnabled()) logger.debug("failed to convert the message charset", exc);
        content = new String(req.getRawContent());
      }

      // to who did we send the original message ?
      ToHeader toHeader = (ToHeader) req.getHeader(ToHeader.NAME);

      if (toHeader == null) {
        // should never happen
        logger.error("send a request without a to header");
        return false;
      }

      Contact to = opSetPersPresence.resolveContactID(toHeader.getAddress().getURI().toString());

      if (to == null) {
        logger.error(
            "Error received a response from an unknown contact : "
                + toHeader.getAddress().getURI().toString()
                + " : "
                + responseEvent.getResponse().getStatusCode()
                + " "
                + responseEvent.getResponse().getReasonPhrase());

        // error for delivering the message
        fireMessageDeliveryFailed(
            // we don't know what message it concerns
            createMessage(content), to, MessageDeliveryFailedEvent.INTERNAL_ERROR);
        return false;
      }

      // we retrieve the original message
      String key = ((CallIdHeader) req.getHeader(CallIdHeader.NAME)).getCallId();

      Message newMessage = sentMsg.get(key);

      if (newMessage == null) {
        // should never happen
        logger.error("Couldn't find the message sent");

        // error for delivering the message
        fireMessageDeliveryFailed(
            // we don't know what message it is
            createMessage(content), to, MessageDeliveryFailedEvent.INTERNAL_ERROR);
        return true;
      }

      // status 401/407 = proxy authentification
      if (status >= 400 && status != 401 && status != 407) {
        if (logger.isInfoEnabled())
          logger.info(
              responseEvent.getResponse().getStatusCode()
                  + " "
                  + responseEvent.getResponse().getReasonPhrase());

        // error for delivering the message
        MessageDeliveryFailedEvent evt =
            new MessageDeliveryFailedEvent(
                newMessage,
                to,
                MessageDeliveryFailedEvent.NETWORK_FAILURE,
                System.currentTimeMillis(),
                responseEvent.getResponse().getStatusCode()
                    + " "
                    + responseEvent.getResponse().getReasonPhrase());
        fireMessageEvent(evt);
        sentMsg.remove(key);
      } else if (status == 401 || status == 407) {
        // proxy ask for authentification
        if (logger.isDebugEnabled())
          logger.debug(
              "proxy asks authentication : "
                  + responseEvent.getResponse().getStatusCode()
                  + " "
                  + responseEvent.getResponse().getReasonPhrase());

        ClientTransaction clientTransaction = responseEvent.getClientTransaction();
        SipProvider sourceProvider = (SipProvider) responseEvent.getSource();

        try {
          processAuthenticationChallenge(
              clientTransaction, responseEvent.getResponse(), sourceProvider);
        } catch (OperationFailedException ex) {
          logger.error("can't solve the challenge", ex);

          // error for delivering the message
          MessageDeliveryFailedEvent evt =
              new MessageDeliveryFailedEvent(
                  newMessage,
                  to,
                  MessageDeliveryFailedEvent.NETWORK_FAILURE,
                  System.currentTimeMillis(),
                  ex.getMessage());
          fireMessageEvent(evt);
          sentMsg.remove(key);
        }
      } else if (status >= 200) {
        if (logger.isDebugEnabled())
          logger.debug(
              "Ack received from the network : "
                  + responseEvent.getResponse().getStatusCode()
                  + " "
                  + responseEvent.getResponse().getReasonPhrase());

        // we delivered the message
        MessageDeliveredEvent msgDeliveredEvt =
            new MessageDeliveredEvent(newMessage, to, System.currentTimeMillis());

        fireMessageEvent(msgDeliveredEvt);

        // we don't need this message anymore
        sentMsg.remove(key);
      }

      return true;
    }
    @Override
    public boolean processTimeout(TimeoutEvent timeoutEvent) {
      synchronized (messageProcessors) {
        for (SipMessageProcessor listener : messageProcessors)
          if (!listener.processTimeout(timeoutEvent, sentMsg)) return true;
      }

      // this is normaly handled by the SIP stack
      logger.error("Timeout event thrown : " + timeoutEvent.toString());

      if (timeoutEvent.isServerTransaction()) {
        logger.warn("The sender has probably not received our OK");
        return false;
      }

      Request req = timeoutEvent.getClientTransaction().getRequest();

      // get the content
      String content = null;
      try {
        content = new String(req.getRawContent(), getCharset(req));
      } catch (UnsupportedEncodingException ex) {
        logger.warn("failed to convert the message charset", ex);
        content = new String(req.getRawContent());
      }

      // to who this request has been sent ?
      ToHeader toHeader = (ToHeader) req.getHeader(ToHeader.NAME);

      if (toHeader == null) {
        logger.error("received a request without a to header");
        return false;
      }

      Contact to = opSetPersPresence.resolveContactID(toHeader.getAddress().getURI().toString());

      Message failedMessage = null;

      if (to == null) {
        logger.error(
            "timeout on a message sent to an unknown contact : "
                + toHeader.getAddress().getURI().toString());

        // we don't know what message it concerns, so create a new
        // one
        failedMessage = createMessage(content);
      } else {
        // try to retrieve the original message
        String key = ((CallIdHeader) req.getHeader(CallIdHeader.NAME)).getCallId();
        failedMessage = sentMsg.get(key);

        if (failedMessage == null) {
          // should never happen
          logger.error("Couldn't find the sent message.");

          // we don't know what the message is so create a new one
          // based on the content of the failed request.
          failedMessage = createMessage(content);
        }
      }

      // error for delivering the message
      fireMessageDeliveryFailed(
          // we don't know what message it concerns
          failedMessage, to, MessageDeliveryFailedEvent.INTERNAL_ERROR);
      return true;
    }