/**
   * Place to put some hacks if needed on incoming requests.
   *
   * @param event the incoming request event.
   * @return status <code>true</code> if we don't need to process this message, just discard it and
   *     <code>false</code> otherwise.
   */
  private boolean applyNonConformanceHacks(RequestEvent event) {
    Request request = event.getRequest();
    try {
      /*
       * Max-Forwards is required, yet there are UAs which do not
       * place it. SipProvider#getNewServerTransaction(Request)
       * will throw an exception in the case of a missing
       * Max-Forwards header and this method will eventually just
       * log it thus ignoring the whole event.
       */
      if (request.getHeader(MaxForwardsHeader.NAME) == null) {
        // it appears that some buggy providers do send requests
        // with no Max-Forwards headers, as we are at application level
        // and we know there will be no endless loops
        // there is no problem of adding headers and process normally
        // this messages
        MaxForwardsHeader maxForwards =
            SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
        request.setHeader(maxForwards);
      }
    } catch (Throwable ex) {
      logger.warn("Cannot apply incoming request modification!", ex);
    }

    try {
      // using asterisk voice mail initial notify for messages
      // is ok, but on the fly received messages their notify comes
      // without subscription-state, so we add it in order to be able to
      // process message.
      if (request.getMethod().equals(Request.NOTIFY)
          && request.getHeader(EventHeader.NAME) != null
          && ((EventHeader) request.getHeader(EventHeader.NAME))
              .getEventType()
              .equals(OperationSetMessageWaitingSipImpl.EVENT_PACKAGE)
          && request.getHeader(SubscriptionStateHeader.NAME) == null) {
        request.addHeader(
            new HeaderFactoryImpl().createSubscriptionStateHeader(SubscriptionStateHeader.ACTIVE));
      }
    } catch (Throwable ex) {
      logger.warn("Cannot apply incoming request modification!", ex);
    }

    try {
      // receiving notify message without subscription state
      // used for keep-alive pings, they have done their job
      // and are no more need. Skip processing them to avoid
      // filling logs with unneeded exceptions.
      if (request.getMethod().equals(Request.NOTIFY)
          && request.getHeader(SubscriptionStateHeader.NAME) == null) {
        return true;
      }
    } catch (Throwable ex) {
      logger.warn("Cannot apply incoming request modification!", ex);
    }

    return false;
  }
 /**
  * Try to find a charset in a MESSAGE request for the text content. If no charset is defined,
  * the default charset for text messages is returned.
  *
  * @param req the MESSAGE request in which to look for a charset
  * @return defined charset in the request or DEFAULT_MIME_ENCODING if no charset is specified
  */
 private String getCharset(Request req) {
   String charset = null;
   Header contentTypeHeader = req.getHeader(ContentTypeHeader.NAME);
   if (contentTypeHeader instanceof ContentTypeHeader)
     charset = ((ContentTypeHeader) contentTypeHeader).getParameter("charset");
   if (charset == null) charset = DEFAULT_MIME_ENCODING;
   return charset;
 }
  /**
   * Sends <tt>messageRequest</tt> to the specified destination and logs <tt>messageContent</tt> for
   * later use.
   *
   * @param messageRequest the <tt>SipRequest</tt> that we are about to send.
   * @param to the Contact that we are sending <tt>messageRequest</tt> to.
   * @param messageContent the SC <tt>Message</tt> that was used to create the <tt>Request</tt> .
   * @throws TransactionUnavailableException if we fail creating the transaction required to send
   *     <tt>messageRequest</tt>.
   * @throws SipException if we fail sending <tt>messageRequest</tt>.
   */
  void sendMessageRequest(Request messageRequest, Contact to, Message messageContent)
      throws TransactionUnavailableException, SipException {
    // Transaction
    ClientTransaction messageTransaction;
    SipProvider jainSipProvider = this.sipProvider.getDefaultJainSipProvider();

    messageTransaction = jainSipProvider.getNewClientTransaction(messageRequest);

    // send the message
    messageTransaction.sendRequest();

    // we register the reference to this message to retrieve it when
    // we'll receive the response message
    String key = ((CallIdHeader) messageRequest.getHeader(CallIdHeader.NAME)).getCallId();

    this.sentMsg.put(key, messageContent);
  }
Exemple #4
0
    @Override
    public void run() {
      try {
        logger.logEntry();

        // From
        FromHeader fromHeader = null;
        try {
          // this keep alive task only makes sense in case we have
          // a registrar so we deliberately use our AOR and do not
          // use the getOurSipAddress() method.
          fromHeader =
              provider
                  .getHeaderFactory()
                  .createFromHeader(
                      provider.getRegistrarConnection().getAddressOfRecord(),
                      SipMessageFactory.generateLocalTag());
        } catch (ParseException ex) {
          // this should never happen so let's just log and bail.
          logger.error("Failed to generate a from header for " + "our register request.", ex);
          return;
        }

        // Call ID Header
        CallIdHeader callIdHeader = provider.getDefaultJainSipProvider().getNewCallId();

        // CSeq Header
        CSeqHeader cSeqHeader = null;
        try {
          cSeqHeader =
              provider.getHeaderFactory().createCSeqHeader(getNextCSeqValue(), Request.OPTIONS);
        } catch (ParseException ex) {
          // Should never happen
          logger.error("Corrupt Sip Stack", ex);
          return;
        } catch (InvalidArgumentException ex) {
          // Should never happen
          logger.error("The application is corrupt", ex);
          return;
        }

        // To Header
        ToHeader toHeader = null;
        try {
          // this request isn't really going anywhere so we put our
          // own address in the To Header.
          toHeader = provider.getHeaderFactory().createToHeader(fromHeader.getAddress(), null);
        } catch (ParseException ex) {
          logger.error("Could not create a To header for address:" + fromHeader.getAddress(), ex);
          return;
        }

        // MaxForwardsHeader
        MaxForwardsHeader maxForwardsHeader = provider.getMaxForwardsHeader();
        // Request
        Request request = null;
        try {
          // create a host-only uri for the request uri header.
          String domain = ((SipURI) toHeader.getAddress().getURI()).getHost();

          // request URI
          SipURI requestURI = provider.getAddressFactory().createSipURI(null, domain);

          // Via Headers
          ArrayList<ViaHeader> viaHeaders = provider.getLocalViaHeaders(requestURI);

          request =
              provider
                  .getMessageFactory()
                  .createRequest(
                      requestURI,
                      Request.OPTIONS,
                      callIdHeader,
                      cSeqHeader,
                      fromHeader,
                      toHeader,
                      viaHeaders,
                      maxForwardsHeader);

          if (logger.isDebugEnabled()) logger.debug("Created OPTIONS request " + request);
        } catch (ParseException ex) {
          logger.error("Could not create an OPTIONS request!", ex);
          return;
        }

        Iterator<String> supportedMethods = provider.getSupportedMethods().iterator();

        // add to the allows header all methods that we support
        while (supportedMethods.hasNext()) {
          String method = supportedMethods.next();

          // don't support REGISTERs
          if (method.equals(Request.REGISTER)) continue;

          request.addHeader(provider.getHeaderFactory().createAllowHeader(method));
        }

        Iterator<String> events = provider.getKnownEventsList().iterator();

        synchronized (provider.getKnownEventsList()) {
          while (events.hasNext()) {
            String event = events.next();

            request.addHeader(provider.getHeaderFactory().createAllowEventsHeader(event));
          }
        }

        // Transaction
        ClientTransaction optionsTrans = null;
        try {
          optionsTrans = provider.getDefaultJainSipProvider().getNewClientTransaction(request);
        } catch (TransactionUnavailableException ex) {
          logger.error("Could not create options transaction!\n", ex);
          return;
        }
        try {
          optionsTrans.sendRequest();
          if (logger.isDebugEnabled()) logger.debug("sent request= " + request);
        } catch (SipException ex) {
          logger.error("Could not send out the options request!", ex);

          if (ex.getCause() instanceof IOException) {
            // IOException problem with network
            disconnect();
          }

          return;
        }
      } catch (Exception ex) {
        logger.error("Cannot send OPTIONS keep alive", ex);
      }
    }
    /**
     * 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;
    }
    /**
     * 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;
    }
    @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;
    }
  /**
   * Construct a <tt>Request</tt> represent a new message.
   *
   * @param to the <tt>Contact</tt> to send <tt>message</tt> to
   * @param message the <tt>Message</tt> to send.
   * @return a Message Request destined to the contact
   * @throws OperationFailedException if an error occurred during the creation of the request
   */
  Request createMessageRequest(Contact to, Message message) throws OperationFailedException {
    Address toAddress = null;
    try {
      toAddress = sipProvider.parseAddressString(to.getAddress());
    } catch (ParseException exc) {
      // Shouldn't happen
      logger.error("An unexpected error occurred while" + "constructing the address", exc);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the address",
          OperationFailedException.INTERNAL_ERROR,
          exc);
    }

    // Call ID
    CallIdHeader callIdHeader = this.sipProvider.getDefaultJainSipProvider().getNewCallId();

    // CSeq
    CSeqHeader cSeqHeader = null;

    try {
      // protect seqN
      synchronized (this) {
        cSeqHeader = this.sipProvider.getHeaderFactory().createCSeqHeader(seqN++, Request.MESSAGE);
      }
    } catch (InvalidArgumentException ex) {
      // Shouldn't happen
      logger.error("An unexpected error occurred while" + "constructing the CSeqHeadder", ex);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the CSeqHeadder",
          OperationFailedException.INTERNAL_ERROR,
          ex);
    } catch (ParseException exc) {
      // shouldn't happen
      logger.error("An unexpected error occurred while" + "constructing the CSeqHeadder", exc);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the CSeqHeadder",
          OperationFailedException.INTERNAL_ERROR,
          exc);
    }

    // FromHeader and ToHeader
    String localTag = SipMessageFactory.generateLocalTag();
    FromHeader fromHeader = null;
    ToHeader toHeader = null;
    try {
      // FromHeader
      fromHeader =
          this.sipProvider
              .getHeaderFactory()
              .createFromHeader(sipProvider.getOurSipAddress(toAddress), localTag);

      // ToHeader
      toHeader = this.sipProvider.getHeaderFactory().createToHeader(toAddress, null);
    } catch (ParseException ex) {
      // these two should never happen.
      logger.error(
          "An unexpected error occurred while" + "constructing the FromHeader or ToHeader", ex);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the FromHeader or ToHeader",
          OperationFailedException.INTERNAL_ERROR,
          ex);
    }

    // ViaHeaders
    ArrayList<ViaHeader> viaHeaders = this.sipProvider.getLocalViaHeaders(toAddress);

    // MaxForwards
    MaxForwardsHeader maxForwards = this.sipProvider.getMaxForwardsHeader();

    // Content params
    ContentTypeHeader contTypeHeader;
    ContentLengthHeader contLengthHeader;
    try {
      contTypeHeader =
          this.sipProvider
              .getHeaderFactory()
              .createContentTypeHeader(getType(message), getSubType(message));

      if (!DEFAULT_MIME_ENCODING.equalsIgnoreCase(message.getEncoding()))
        contTypeHeader.setParameter("charset", message.getEncoding());

      contLengthHeader =
          this.sipProvider.getHeaderFactory().createContentLengthHeader(message.getSize());
    } catch (ParseException ex) {
      // these two should never happen.
      logger.error("An unexpected error occurred while" + "constructing the content headers", ex);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the content headers",
          OperationFailedException.INTERNAL_ERROR,
          ex);
    } catch (InvalidArgumentException exc) {
      // these two should never happen.
      logger.error(
          "An unexpected error occurred while" + "constructing the content length header", exc);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the content length header",
          OperationFailedException.INTERNAL_ERROR,
          exc);
    }

    Request req;
    try {
      req =
          this.sipProvider
              .getMessageFactory()
              .createRequest(
                  toHeader.getAddress().getURI(),
                  Request.MESSAGE,
                  callIdHeader,
                  cSeqHeader,
                  fromHeader,
                  toHeader,
                  viaHeaders,
                  maxForwards,
                  contTypeHeader,
                  message.getRawData());
    } catch (ParseException ex) {
      // shouldn't happen
      logger.error("Failed to create message Request!", ex);
      throw new OperationFailedException(
          "Failed to create message Request!", OperationFailedException.INTERNAL_ERROR, ex);
    }

    req.addHeader(contLengthHeader);

    return req;
  }
  /**
   * Find the <tt>ProtocolProviderServiceSipImpl</tt> (one of our "candidate recipient" listeners)
   * which this <tt>request</tt> should be dispatched to. The strategy is to look first at the
   * request URI, and then at the To field to find a matching candidate for dispatching. Note that
   * this method takes a <tt>Request</tt> as param, and not a <tt>ServerTransaction</tt>, because
   * sometimes <tt>RequestEvent</tt>s have no associated <tt>ServerTransaction</tt>.
   *
   * @param request the <tt>Request</tt> to find a recipient for.
   * @return a suitable <tt>ProtocolProviderServiceSipImpl</tt>.
   */
  private ProtocolProviderServiceSipImpl findTargetFor(Request request) {
    if (request == null) {
      logger.error("request shouldn't be null.");
      return null;
    }

    List<ProtocolProviderServiceSipImpl> currentListenersCopy =
        new ArrayList<ProtocolProviderServiceSipImpl>(this.getSipListeners());

    // Let's first narrow down candidate choice by comparing
    // addresses and ports (no point in delivering to a provider with a
    // non matching IP address  since they will reject it anyway).
    filterByAddress(currentListenersCopy, request);

    if (currentListenersCopy.size() == 0) {
      logger.error("no listeners");
      return null;
    }

    URI requestURI = request.getRequestURI();

    if (requestURI.isSipURI()) {
      String requestUser = ((SipURI) requestURI).getUser();

      List<ProtocolProviderServiceSipImpl> candidates =
          new ArrayList<ProtocolProviderServiceSipImpl>();

      // check if the Request-URI username is
      // one of ours usernames
      for (ProtocolProviderServiceSipImpl listener : currentListenersCopy) {
        String ourUserID = listener.getAccountID().getUserID();
        // logger.trace(ourUserID + " *** " + requestUser);
        if (ourUserID.equals(requestUser)) {
          if (logger.isTraceEnabled())
            logger.trace("suitable candidate found: " + listener.getAccountID());
          candidates.add(listener);
        }
      }

      // the perfect match
      // every other case is approximation
      if (candidates.size() == 1) {
        ProtocolProviderServiceSipImpl perfectMatch = candidates.get(0);

        if (logger.isTraceEnabled())
          logger.trace("Will dispatch to \"" + perfectMatch.getAccountID() + "\"");
        return perfectMatch;
      }

      // more than one account match
      if (candidates.size() > 1) {
        // check if a custom param exists in the contact
        // address (set for registrar accounts)
        for (ProtocolProviderServiceSipImpl candidate : candidates) {
          String hostValue =
              ((SipURI) requestURI).getParameter(SipStackSharing.CONTACT_ADDRESS_CUSTOM_PARAM_NAME);
          if (hostValue == null) continue;
          if (hostValue.equals(candidate.getContactAddressCustomParamValue())) {
            if (logger.isTraceEnabled())
              logger.trace(
                  "Will dispatch to \""
                      + candidate.getAccountID()
                      + "\" because "
                      + "\" the custom param was set");
            return candidate;
          }
        }

        // Past this point, our guess is not reliable. We try to find
        // the "least worst" match based on parameters like the To field

        // check if the To header field host part
        // matches any of our SIP hosts
        for (ProtocolProviderServiceSipImpl candidate : candidates) {
          URI fromURI = ((FromHeader) request.getHeader(FromHeader.NAME)).getAddress().getURI();
          if (fromURI.isSipURI() == false) continue;
          SipURI ourURI = (SipURI) candidate.getOurSipAddress((SipURI) fromURI).getURI();
          String ourHost = ourURI.getHost();

          URI toURI = ((ToHeader) request.getHeader(ToHeader.NAME)).getAddress().getURI();
          if (toURI.isSipURI() == false) continue;
          String toHost = ((SipURI) toURI).getHost();

          // logger.trace(toHost + "***" + ourHost);
          if (toHost.equals(ourHost)) {
            if (logger.isTraceEnabled())
              logger.trace(
                  "Will dispatch to \""
                      + candidate.getAccountID()
                      + "\" because "
                      + "host in the To: is the same as in our AOR");
            return candidate;
          }
        }

        // fallback on the first candidate
        ProtocolProviderServiceSipImpl target = candidates.iterator().next();
        logger.info(
            "Will randomly dispatch to \""
                + target.getAccountID()
                + "\" because there is ambiguity on the username from"
                + " the Request-URI");
        if (logger.isTraceEnabled()) logger.trace("\n" + request);
        return target;
      }

      // fallback on any account
      ProtocolProviderServiceSipImpl target = currentListenersCopy.iterator().next();
      if (logger.isDebugEnabled())
        logger.debug(
            "Will randomly dispatch to \""
                + target.getAccountID()
                + "\" because the username in the Request-URI "
                + "is unknown or empty");
      if (logger.isTraceEnabled()) logger.trace("\n" + request);
      return target;
    } else {
      logger.error("Request-URI is not a SIP URI, dropping");
    }
    return null;
  }
  /**
   * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate
   * recipient" listeners.
   *
   * @param event the event received for a <tt>SipProvider</tt>.
   */
  public void processRequest(RequestEvent event) {
    try {
      Request request = event.getRequest();
      if (logger.isTraceEnabled()) logger.trace("received request: " + request.getMethod());

      /*
       * Create the transaction if it doesn't exist yet. If it is a
       * dialog-creating request, the dialog will also be automatically
       * created by the stack.
       */
      if (event.getServerTransaction() == null) {
        try {
          // apply some hacks if needed on incoming request
          // to be compliant with some servers/clients
          // if needed stop further processing.
          if (applyNonConformanceHacks(event)) return;

          SipProvider source = (SipProvider) event.getSource();
          ServerTransaction transaction = source.getNewServerTransaction(request);

          /*
           * Update the event, otherwise getServerTransaction() and
           * getDialog() will still return their previous value.
           */
          event = new RequestEvent(source, transaction, transaction.getDialog(), request);
        } catch (SipException ex) {
          logger.error(
              "couldn't create transaction, please report "
                  + "this to [email protected]",
              ex);
        }
      }

      ProtocolProviderServiceSipImpl service = getServiceData(event.getServerTransaction());
      if (service != null) {
        service.processRequest(event);
      } else {
        service = findTargetFor(request);
        if (service == null) {
          logger.error("couldn't find a ProtocolProviderServiceSipImpl " + "to dispatch to");
          if (event.getServerTransaction() != null) event.getServerTransaction().terminate();
        } else {

          /*
           * Mark the dialog for the dispatching of later in-dialog
           * requests. If there is no dialog, we need to mark the
           * request to dispatch a possible timeout when sending the
           * response.
           */
          Object container = event.getDialog();
          if (container == null) container = request;
          SipApplicationData.setApplicationData(container, SipApplicationData.KEY_SERVICE, service);

          service.processRequest(event);
        }
      }
    } catch (Throwable exc) {

      /*
       * Any exception thrown within our code should be caught here so
       * that we could log it rather than interrupt stack activity with
       * it.
       */
      this.logApplicationException(DialogTerminatedEvent.class, exc);

      // Unfortunately, death can hardly be ignored.
      if (exc instanceof ThreadDeath) throw (ThreadDeath) exc;
    }
  }