/**
     * Attempts to re-generate the corresponding request with the proper credentials.
     *
     * @param clientTransaction the corresponding transaction
     * @param response the challenge
     * @param jainSipProvider the provider that received the challenge
     * @throws OperationFailedException if processing the authentication challenge fails.
     */
    private void processAuthenticationChallenge(
        ClientTransaction clientTransaction, Response response, SipProvider jainSipProvider)
        throws OperationFailedException {
      try {
        if (logger.isDebugEnabled()) logger.debug("Authenticating a message request.");

        ClientTransaction retryTran = null;

        // we synch here to protect seqN increment
        synchronized (this) {
          retryTran =
              sipProvider
                  .getSipSecurityManager()
                  .handleChallenge(response, clientTransaction, jainSipProvider, seqN++);
        }

        if (retryTran == null) {
          if (logger.isTraceEnabled()) logger.trace("No password supplied or error occured!");
          return;
        }

        retryTran.sendRequest();
        return;
      } catch (Exception exc) {
        logger.error("We failed to authenticate a message request.", exc);

        throw new OperationFailedException(
            "Failed to authenticate" + "a message request",
            OperationFailedException.INTERNAL_ERROR,
            exc);
      }
    }
  /**
   * Creates and sends a SUBSCRIBE request to the subscription <tt>Address</tt>/Request URI of a
   * specific <tt>Subscription</tt> in order to request receiving event notifications and adds the
   * specified <tt>Subscription</tt> to the list of subscriptions managed by this instance. The
   * added <tt>Subscription</tt> may later receive notifications to process the <tt>Request</tt>s
   * and/or <tt>Response</tt>s which constitute the signaling session associated with it. If the
   * attempt to create and send the SUBSCRIBE request fails, the specified <tt>Subscription</tt> is
   * not added to the list of subscriptions managed by this instance.
   *
   * @param subscription a <tt>Subscription</tt> which specifies the properties of the SUBSCRIBE
   *     request to be created and sent, to be added to the list of subscriptions managed by this
   *     instance
   * @throws OperationFailedException if we fail constructing or sending the subscription request.
   */
  public void subscribe(Subscription subscription) throws OperationFailedException {
    Dialog dialog = subscription.getDialog();

    if ((dialog != null) && DialogState.TERMINATED.equals(dialog.getState())) dialog = null;

    // create the subscription
    ClientTransaction subscribeTransaction = null;
    try {
      subscribeTransaction =
          (dialog == null)
              ? createSubscription(subscription, subscriptionDuration)
              : createSubscription(subscription, dialog, subscriptionDuration);
    } catch (OperationFailedException ex) {
      ProtocolProviderServiceSipImpl.throwOperationFailedException(
          "Failed to create the subscription", OperationFailedException.INTERNAL_ERROR, ex, logger);
    }

    // we register the contact to find him when the OK will arrive
    CallIdHeader callIdHeader =
        (CallIdHeader) subscribeTransaction.getRequest().getHeader(CallIdHeader.NAME);
    String callId = callIdHeader.getCallId();
    addSubscription(callId, subscription);

    // send the message
    try {
      if (dialog == null) subscribeTransaction.sendRequest();
      else dialog.sendRequest(subscribeTransaction);
    } catch (SipException ex) {
      // this contact will never been accepted or rejected
      removeSubscription(callId, subscription);

      ProtocolProviderServiceSipImpl.throwOperationFailedException(
          "Failed to send the subscription", OperationFailedException.NETWORK_FAILURE, ex, logger);
    }
  }
  /**
   * 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);
  }
示例#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);
      }
    }
  /**
   * 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;
  }