Esempio n. 1
0
  /**
   * Checks for certain params existence in the value, and replace them with real values obtained
   * from <tt>request</tt>.
   *
   * @param value the value of the header param
   * @param request the request we are processing
   * @return the value with replaced params
   */
  private static String processParams(String value, Request request) {
    if (value.indexOf("${from.address}") != -1) {
      FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);

      if (fromHeader != null) {
        value = value.replace("${from.address}", fromHeader.getAddress().getURI().toString());
      }
    }

    if (value.indexOf("${from.userID}") != -1) {
      FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);

      if (fromHeader != null) {
        URI fromURI = fromHeader.getAddress().getURI();
        String fromAddr = fromURI.toString();

        // strips sip: or sips:
        if (fromURI.isSipURI()) {
          fromAddr = fromAddr.replaceFirst(fromURI.getScheme() + ":", "");
        }

        // take the userID part
        int index = fromAddr.indexOf('@');
        if (index > -1) fromAddr = fromAddr.substring(0, index);

        value = value.replace("${from.userID}", fromAddr);
      }
    }

    if (value.indexOf("${to.address}") != -1) {
      ToHeader toHeader = (ToHeader) request.getHeader(ToHeader.NAME);

      if (toHeader != null) {
        value = value.replace("${to.address}", toHeader.getAddress().getURI().toString());
      }
    }

    if (value.indexOf("${to.userID}") != -1) {
      ToHeader toHeader = (ToHeader) request.getHeader(ToHeader.NAME);

      if (toHeader != null) {
        URI toURI = toHeader.getAddress().getURI();
        String toAddr = toURI.toString();

        // strips sip: or sips:
        if (toURI.isSipURI()) {
          toAddr = toAddr.replaceFirst(toURI.getScheme() + ":", "");
        }

        // take the userID part
        int index = toAddr.indexOf('@');
        if (index > -1) toAddr = toAddr.substring(0, index);

        value = value.replace("${to.userID}", toAddr);
      }
    }

    return value;
  }
Esempio n. 2
0
  /**
   * 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;
  }
  /**
   * Populates a specific <tt>Request</tt> instance with the headers common to dialog-creating
   * <tt>Request</tt>s and ones sent inside existing dialogs and specific to the general event
   * package subscription functionality that this instance and a specific <tt>Subscription</tt>
   * represent.
   *
   * @param req the <tt>Request</tt> instance to be populated with common headers and ones specific
   *     to the event package of a specific <tt>Subscription</tt>
   * @param subscription the <tt>Subscription</tt> which is to be described in the specified
   *     <tt>Request</tt> i.e. its properties are to be used to populate the specified
   *     <tt>Request</tt>
   * @param expires the subscription duration to be set into the Expires header of the specified
   *     SUBSCRIBE <tt>Request</tt>
   * @throws OperationFailedException if we fail parsing or populating the subscription request.
   */
  protected void populateSubscribeRequest(Request req, Subscription subscription, int expires)
      throws OperationFailedException {
    HeaderFactory headerFactory = protocolProvider.getHeaderFactory();

    // Event
    EventHeader evHeader;
    try {
      evHeader = headerFactory.createEventHeader(eventPackage);

      String eventId = subscription.getEventId();
      if (eventId != null) evHeader.setEventId(eventId);
    } catch (ParseException e) {
      // these two should never happen.
      logger.error("An unexpected error occurred while" + "constructing the EventHeader", e);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the EventHeader",
          OperationFailedException.INTERNAL_ERROR,
          e);
    }
    req.setHeader(evHeader);

    // Accept
    AcceptHeader accept;
    try {
      accept = headerFactory.createAcceptHeader("application", contentSubType);
    } catch (ParseException e) {
      logger.error("wrong accept header", e);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the AcceptHeader",
          OperationFailedException.INTERNAL_ERROR,
          e);
    }
    req.setHeader(accept);

    // Expires
    ExpiresHeader expHeader;
    try {
      expHeader = headerFactory.createExpiresHeader(expires);
    } catch (InvalidArgumentException e) {
      logger.error("Invalid expires value: " + expires, e);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the ExpiresHeader",
          OperationFailedException.INTERNAL_ERROR,
          e);
    }
    req.setHeader(expHeader);
  }
 /**
  * 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;
 }
  /**
   * Creates a new SUBSCRIBE request in the form of a <tt>ClientTransaction</tt> with the parameters
   * of a specific <tt>Subscription</tt>.
   *
   * @param subscription the <tt>Subscription</tt> to be described in a SUBSCRIBE request
   * @param dialog the <tt>Dialog</tt> with which this request should be associated
   * @param expires the subscription duration of the SUBSCRIBE request to be created
   * @return a new <tt>ClientTransaction</tt> initialized with a new SUBSCRIBE request which matches
   *     the parameters of the specified <tt>Subscription</tt> and is associated with the specified
   *     <tt>Dialog</tt>
   * @throws OperationFailedException if the message could not be generated
   */
  private ClientTransaction createSubscription(
      Subscription subscription, Dialog dialog, int expires) throws OperationFailedException {
    Request req = messageFactory.createRequest(dialog, Request.SUBSCRIBE);

    // Address
    Address toAddress = dialog.getRemoteTarget();
    // no Contact field
    if (toAddress == null) toAddress = dialog.getRemoteParty();

    // MaxForwards
    MaxForwardsHeader maxForwards = protocolProvider.getMaxForwardsHeader();
    req.setHeader(maxForwards);

    /*
     * Create the transaction and then add the via header as recommended by
     * the jain-sip documentation at
     * http://snad.ncsl.nist.gov/proj/iptel/jain-sip-1.2/javadoc
     * /javax/sip/Dialog.html#createRequest(String).
     */
    ClientTransaction transac = null;
    try {
      transac = protocolProvider.getDefaultJainSipProvider().getNewClientTransaction(req);
    } catch (TransactionUnavailableException ex) {
      logger.error(
          "Failed to create subscriptionTransaction.\n"
              + "This is most probably a network connection error.",
          ex);
      throw new OperationFailedException(
          "Failed to create the subscription transaction",
          OperationFailedException.NETWORK_FAILURE);
    }

    populateSubscribeRequest(req, subscription, expires);

    return transac;
  }
  /**
   * 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);
  }
Esempio n. 7
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;
  }
Esempio n. 12
0
  /**
   * 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;
  }
Esempio n. 13
0
  /**
   * 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;
    }
  }
Esempio n. 14
0
  /**
   * Attach any custom headers pre configured for the account. Added only to message Requests. The
   * header account properties are of form: ConfigHeader.N.Name=HeaderName
   * ConfigHeader.N.Value=HeaderValue ConfigHeader.N.Method=SIP_MethodName (optional) Where N is the
   * index of the header, multiple custom headers can be added. Name is the header name to use and
   * Value is its value. The optional property is whether to use a specific request method to attach
   * headers to or if missing we will attach it to all requests.
   *
   * @param message the message that we'd like to attach custom headers to.
   * @param protocolProvider the protocol provider to check for configured custom headers.
   */
  static void attachConfigHeaders(
      Message message, ProtocolProviderServiceSipImpl protocolProvider) {
    if (message instanceof Response) return;

    Request request = (Request) message;

    Map<String, String> props = protocolProvider.getAccountID().getAccountProperties();

    Map<String, Map<String, String>> headers = new HashMap<String, Map<String, String>>();

    // parse the properties into the map where the index is the key
    for (Map.Entry<String, String> entry : props.entrySet()) {
      String pName = entry.getKey();
      String prefStr = entry.getValue();
      String name;
      String ix;

      if (!pName.startsWith(ACC_PROPERTY_CONFIG_HEADER) || prefStr == null) continue;

      prefStr = prefStr.trim();

      if (pName.contains(".")) {
        pName = pName.replaceAll(ACC_PROPERTY_CONFIG_HEADER + ".", "");
        name = pName.substring(pName.lastIndexOf('.') + 1).trim();

        if (!pName.contains(".")) continue;

        ix = pName.substring(0, pName.lastIndexOf('.')).trim();
      } else continue;

      Map<String, String> headerValues = headers.get(ix);

      if (headerValues == null) {
        headerValues = new HashMap<String, String>();
        headers.put(ix, headerValues);
      }

      headerValues.put(name, prefStr);
    }

    // process the found custom headers
    for (Map<String, String> headerValues : headers.values()) {
      String method = headerValues.get(ACC_PROPERTY_CONFIG_HEADER_METHOD);

      // if there is a method setting and is different from
      // current request method skip this header
      // if any of the required properties are missing skip (Name & Value)
      if ((method != null && !request.getMethod().equalsIgnoreCase(method))
          || !headerValues.containsKey(ACC_PROPERTY_CONFIG_HEADER_NAME)
          || !headerValues.containsKey(ACC_PROPERTY_CONFIG_HEADER_VALUE)) continue;

      try {
        String name = headerValues.get(ACC_PROPERTY_CONFIG_HEADER_NAME);
        String value = processParams(headerValues.get(ACC_PROPERTY_CONFIG_HEADER_VALUE), request);

        Header h = request.getHeader(name);

        // makes possible overriding already created headers which
        // are not custom one
        // RouteHeader is used by ProxyRouter/DefaultRouter and we
        // cannot use it as custom header if we want to add it
        if ((h != null && !(h instanceof CustomHeader)) || name.equals(SIPHeaderNames.ROUTE)) {
          request.setHeader(protocolProvider.getHeaderFactory().createHeader(name, value));
        } else request.addHeader(new CustomHeaderList(name, value));
      } catch (Exception e) {
        logger.error("Cannot create custom header", e);
      }
    }
  }
  /**
   * Implements {@link MethodProcessor#processResponse(ResponseEvent)}. Handles only responses to
   * SUBSCRIBE requests because they are the only requests concerning event package subscribers (and
   * the only requests sent by them, for that matter) and if the processing of a given response
   * requires event package-specific handling, delivers the response to the matching
   * <tt>Subscription</tt> instance. Examples of such event package-specific handling include
   * letting the respective <tt>Subscription</tt> handle the success or failure in the establishment
   * of a subscription.
   *
   * @param responseEvent a <tt>ResponseEvent</tt> specifying the SIP <tt>Response</tt> to be
   *     processed
   * @return <tt>true</tt> if the SIP <tt>Response</tt> specified by <tt>responseEvent</tt> was
   *     processed; otherwise, <tt>false</tt>
   */
  @Override
  public boolean processResponse(ResponseEvent responseEvent) {
    Response response = responseEvent.getResponse();

    CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
    if (cseqHeader == null) {
      logger.error("An incoming response did not contain a CSeq header");
      return false;
    }
    if (!Request.SUBSCRIBE.equals(cseqHeader.getMethod())) return false;

    ClientTransaction clientTransaction = responseEvent.getClientTransaction();
    Request request = clientTransaction.getRequest();

    /*
     * Don't handle responses to requests not coming from this event
     * package.
     */
    if (request != null) {
      EventHeader eventHeader = (EventHeader) request.getHeader(EventHeader.NAME);
      if ((eventHeader == null) || !eventPackage.equalsIgnoreCase(eventHeader.getEventType()))
        return false;
    }

    // Find the subscription.
    CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
    String callId = callIdHeader.getCallId();
    Subscription subscription = getSubscription(callId);

    // if it's the response to an unsubscribe message, we just ignore it
    // whatever the response is however if we need to handle a
    // challenge, we do it
    ExpiresHeader expHeader = response.getExpires();
    int statusCode = response.getStatusCode();
    SipProvider sourceProvider = (SipProvider) responseEvent.getSource();
    if (((expHeader != null) && (expHeader.getExpires() == 0))
        || (subscription == null)) // this handle the unsubscription
    // case where we removed the contact
    // from subscribedContacts
    {
      boolean processed = false;

      if ((statusCode == Response.UNAUTHORIZED)
          || (statusCode == Response.PROXY_AUTHENTICATION_REQUIRED)) {
        try {
          processAuthenticationChallenge(clientTransaction, response, sourceProvider);
          processed = true;
        } catch (OperationFailedException e) {
          logger.error("can't handle the challenge", e);
        }
      } else if ((statusCode != Response.OK) && (statusCode != Response.ACCEPTED)) processed = true;

      // any other cases (200/202) will imply a NOTIFY, so we will
      // handle the end of a subscription there
      return processed;
    }

    if ((statusCode >= Response.OK) && (statusCode < Response.MULTIPLE_CHOICES)) {
      // OK (200/202)
      if ((statusCode == Response.OK) || (statusCode == Response.ACCEPTED)) {
        if (expHeader == null) {
          // not conform to rfc3265
          logger.error("no Expires header in this response");
          return false;
        }

        SubscriptionRefreshTask refreshTask = new SubscriptionRefreshTask(subscription);
        subscription.setTimerTask(refreshTask);

        int refreshDelay = expHeader.getExpires();
        // try to keep a margin if the refresh delay allows it
        if (refreshDelay >= (2 * refreshMargin)) refreshDelay -= refreshMargin;
        timer.schedule(refreshTask, refreshDelay * 1000);

        // do it to remember the dialog in case of a polling
        // subscription (which means no call to finalizeSubscription)
        subscription.setDialog(clientTransaction.getDialog());

        subscription.processSuccessResponse(responseEvent, statusCode);
      }
    } else if ((statusCode >= Response.MULTIPLE_CHOICES) && (statusCode < Response.BAD_REQUEST)) {
      if (logger.isInfoEnabled())
        logger.info(
            "Response to subscribe to "
                + subscription.getAddress()
                + ": "
                + response.getReasonPhrase());
    } else if (statusCode >= Response.BAD_REQUEST) {
      // if the response is a 423 response, just re-send the request
      // with a valid expires value
      if (statusCode == Response.INTERVAL_TOO_BRIEF) {
        MinExpiresHeader min = (MinExpiresHeader) response.getHeader(MinExpiresHeader.NAME);

        if (min == null) {
          logger.error("no minimal expires value in this 423 " + "response");
          return false;
        }

        ExpiresHeader exp = request.getExpires();

        try {
          exp.setExpires(min.getExpires());
        } catch (InvalidArgumentException e) {
          logger.error("can't set the new expires value", e);
          return false;
        }

        ClientTransaction transac = null;
        try {
          transac = protocolProvider.getDefaultJainSipProvider().getNewClientTransaction(request);
        } catch (TransactionUnavailableException e) {
          logger.error("can't create the client transaction", e);
          return false;
        }

        try {
          transac.sendRequest();
        } catch (SipException e) {
          logger.error("can't send the new request", e);
          return false;
        }

        return true;
        // UNAUTHORIZED (401/407)
      } else if ((statusCode == Response.UNAUTHORIZED)
          || (statusCode == Response.PROXY_AUTHENTICATION_REQUIRED)) {
        try {
          processAuthenticationChallenge(clientTransaction, response, sourceProvider);
        } catch (OperationFailedException e) {
          logger.error("can't handle the challenge", e);

          removeSubscription(callId, subscription);
          subscription.processFailureResponse(responseEvent, statusCode);
        }
        // 408 480 486 600 603 : non definitive reject
        // others: definitive reject (or not implemented)
      } else {
        if (logger.isDebugEnabled()) logger.debug("error received from the network:\n" + response);

        removeSubscription(callId, subscription);
        subscription.processFailureResponse(responseEvent, statusCode);
      }
    }

    return true;
  }
  /**
   * Implements {@link MethodProcessor#processRequest(RequestEvent)}. Handles only NOTIFY requests
   * because they are the only requests concerning event package subscribers and if the processing
   * of a given request requires event package-specific handling, delivers the request to the
   * matching Subscription instance. Examples of such event package-specific handling include
   * handling the termination of an existing Subscription and processing the bodies of the NOTIFY
   * requests for active Subscriptions.
   *
   * @param requestEvent a <tt>RequestEvent</tt> specifying the SIP <tt>Request</tt> to be processed
   * @return <tt>true</tt> if the SIP <tt>Request</tt> specified by <tt>requestEvent</tt> was
   *     processed; otherwise, <tt>false</tt>
   */
  @Override
  public boolean processRequest(RequestEvent requestEvent) {
    Request request = requestEvent.getRequest();

    EventHeader eventHeader = (EventHeader) request.getHeader(EventHeader.NAME);
    if ((eventHeader == null) || !eventPackage.equalsIgnoreCase(eventHeader.getEventType())) {
      /*
       * We are not concerned by this request, perhaps another listener
       * is. So don't send a 489 / Bad event answer here.
       */
      return false;
    }

    if (!Request.NOTIFY.equals(request.getMethod())) return false;

    if (logger.isDebugEnabled()) logger.debug("notify received");

    SubscriptionStateHeader sstateHeader =
        (SubscriptionStateHeader) request.getHeader(SubscriptionStateHeader.NAME);
    // notify must contain one (rfc3265)
    if (sstateHeader == null) {
      logger.error("no subscription state in this request");
      return false;
    }
    String sstate = sstateHeader.getState();

    ServerTransaction serverTransaction = getOrCreateServerTransaction(requestEvent);

    // first handle the case of a contact still pending
    // it's possible if the NOTIFY arrives before the OK
    CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
    String callId = callIdHeader.getCallId();
    Subscription subscription = getSubscription(callId);

    // see if the notify correspond to an existing subscription
    if ((subscription == null) && !SubscriptionStateHeader.TERMINATED.equalsIgnoreCase(sstate)) {
      if (logger.isDebugEnabled()) logger.debug("subscription not found for callId " + callId);

      // send a 481 response (rfc3625)
      Response response;
      try {
        response =
            protocolProvider
                .getMessageFactory()
                .createResponse(Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST, request);
      } catch (ParseException e) {
        logger.error("failed to create the 481 response", e);
        return false;
      }

      try {
        serverTransaction.sendResponse(response);
      } catch (SipException e) {
        logger.error("failed to send the response", e);
      } catch (InvalidArgumentException e) {
        // should not happen
        logger.error("invalid argument provided while trying to send the response", e);
      }
      return true;
    }

    // if we don't understand the content
    ContentTypeHeader ctheader = (ContentTypeHeader) request.getHeader(ContentTypeHeader.NAME);
    if ((ctheader != null) && !ctheader.getContentSubType().equalsIgnoreCase(contentSubType)) {
      // send a 415 response (rfc3261)
      Response response;
      try {
        response =
            protocolProvider
                .getMessageFactory()
                .createResponse(Response.UNSUPPORTED_MEDIA_TYPE, request);
      } catch (ParseException e) {
        logger.error("failed to create the OK response", e);
        return false;
      }

      // we want PIDF
      AcceptHeader acceptHeader;
      try {
        acceptHeader =
            protocolProvider.getHeaderFactory().createAcceptHeader("application", contentSubType);
      } catch (ParseException e) {
        // should not happen
        logger.error("failed to create the accept header", e);
        return false;
      }
      response.setHeader(acceptHeader);

      try {
        serverTransaction.sendResponse(response);
      } catch (SipException e) {
        logger.error("failed to send the response", e);
      } catch (InvalidArgumentException e) {
        // should not happen
        logger.error("invalid argument provided while trying" + " to send the response", e);
      }
    }

    // if the presentity doesn't want of us anymore
    if (SubscriptionStateHeader.TERMINATED.equalsIgnoreCase(sstate)) {
      // if we requested this end of subscription, subscription == null
      if (subscription != null) {
        removeSubscription(callId, subscription);
        subscription.processTerminatedRequest(requestEvent, sstateHeader.getReasonCode());
      }
    }

    // send an OK response
    Response response;
    try {
      response = protocolProvider.getMessageFactory().createResponse(Response.OK, request);
    } catch (ParseException e) {
      logger.error("failed to create the OK response", e);
      return false;
    }

    try {
      serverTransaction.sendResponse(response);
    } catch (SipException e) {
      logger.error("failed to send the response", e);
    } catch (InvalidArgumentException e) {
      // should not happen
      logger.error("invalid argument provided while trying to send the response", e);
    }

    // transform the presence document in new presence status
    if (subscription != null)
      subscription.processActiveRequest(requestEvent, request.getRawContent());

    return true;
  }