예제 #1
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 processTimeout(TimeoutEvent event) {
    try {
      Transaction transaction;
      if (event.isServerTransaction()) {
        transaction = event.getServerTransaction();
      } else {
        transaction = event.getClientTransaction();
      }

      ProtocolProviderServiceSipImpl recipient = getServiceData(transaction);
      if (recipient == null) {
        logger.error(
            "We received a timeout which wasn't "
                + "marked, please report this to "
                + "*****@*****.**");
      } else {
        recipient.processTimeout(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);
    }
  }
예제 #2
0
    /** The real task work, replace listening point. */
    public void run() {
      // if the provider is still unregistering it most probably won't
      // successes until we re-init the LP
      if (protocolProvider.getRegistrationState() == RegistrationState.UNREGISTERING) {
        String transport = protocolProvider.getRegistrarConnection().getTransport();

        ListeningPoint old = getLP(transport);

        try {
          stack.deleteListeningPoint(old);
        } catch (Throwable t) {
          logger.warn("Error replacing ListeningPoint for " + transport, t);
        }

        try {
          ListeningPoint tcpLP =
              stack.createListeningPoint(
                  NetworkUtils.IN_ADDR_ANY,
                  transport.equals(ListeningPoint.TCP)
                      ? getPreferredClearPort()
                      : getPreferredSecurePort(),
                  transport);
          clearJainSipProvider.addListeningPoint(tcpLP);
        } catch (Throwable t) {
          logger.warn(
              "Error replacing ListeningPoint for "
                  + protocolProvider.getRegistrarConnection().getTransport(),
              t);
        }
      }

      resetListeningPointsTimers.remove(protocolProvider.getRegistrarConnection().getTransport());
    }
예제 #3
0
  /**
   * Creates a new instance of this class using the specified parent <tt>protocolProvider</tt>.
   *
   * @param protocolProvider a reference to the <tt>ProtocolProviderServiceSipImpl</tt> instance
   *     that created us.
   */
  public ClientCapabilities(ProtocolProviderServiceSipImpl protocolProvider) {
    this.provider = protocolProvider;

    provider.registerMethodProcessor(Request.OPTIONS, this);

    registrationListener = new RegistrationListener();
    provider.addRegistrationStateChangeListener(registrationListener);
  }
예제 #4
0
 /**
  * Notified when registration state changed for a provider.
  *
  * @param evt
  */
 public void registrationStateChanged(RegistrationStateChangeEvent evt) {
   if (evt.getNewState() == RegistrationState.UNREGISTERING) {
     new Timer().schedule(this, TIME_FOR_PP_TO_UNREGISTER);
   } else {
     protocolProvider.removeRegistrationStateChangeListener(this);
     resetListeningPointsTimers.remove(protocolProvider.getRegistrarConnection().getTransport());
   }
 }
예제 #5
0
  /** Fire event that connection has failed and we had to unregister the protocol provider. */
  private void disconnect() {
    // don't alert the user if we're already off
    if (provider.getRegistrationState().equals(RegistrationState.UNREGISTERED)) {
      return;
    }

    provider
        .getRegistrarConnection()
        .setRegistrationState(
            RegistrationState.CONNECTION_FAILED,
            RegistrationStateChangeEvent.REASON_NOT_SPECIFIED,
            "A timeout occurred while trying to connect to the server.");
  }
  /**
   * Creates an instance of this operation set.
   *
   * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt> that created us and that
   *     we'll use for retrieving the underlying aim connection.
   */
  OperationSetBasicInstantMessagingSipImpl(ProtocolProviderServiceSipImpl provider) {
    this.sipProvider = provider;

    provider.addRegistrationStateChangeListener(new RegistrationStateListener());

    offlineMessageSupported =
        provider.getAccountID().getAccountPropertyBoolean("OFFLINE_MSG_SUPPORTED", false);

    sipProvider.registerMethodProcessor(
        Request.MESSAGE, new BasicInstantMessagingMethodProcessor());

    this.sipStatusEnum = sipProvider.getSipStatusEnum();
  }
예제 #7
0
  /**
   * Receives options requests and replies with an OK response containing methods that we support.
   *
   * @param requestEvent the incoming options request.
   * @return <tt>true</tt> if request has been successfully processed, <tt>false</tt> otherwise
   */
  @Override
  public boolean processRequest(RequestEvent requestEvent) {
    Response optionsOK = null;
    try {
      optionsOK =
          provider.getMessageFactory().createResponse(Response.OK, requestEvent.getRequest());

      // add to the allows header all methods that we support
      for (String method : provider.getSupportedMethods()) {
        // don't support REGISTERs
        if (!method.equals(Request.REGISTER))
          optionsOK.addHeader(provider.getHeaderFactory().createAllowHeader(method));
      }

      Iterable<String> knownEventsList = provider.getKnownEventsList();

      synchronized (knownEventsList) {
        for (String event : knownEventsList)
          optionsOK.addHeader(provider.getHeaderFactory().createAllowEventsHeader(event));
      }
    } catch (ParseException ex) {
      // What else could we do apart from logging?
      logger.warn("Failed to create an incoming OPTIONS request", ex);
      return false;
    }

    try {
      SipStackSharing.getOrCreateServerTransaction(requestEvent).sendResponse(optionsOK);
    } catch (TransactionUnavailableException ex) {
      // this means that we received an OPTIONS request outside the scope
      // of a transaction which could mean that someone is simply sending
      // us b****hit to keep a NAT connection alive, so let's not get too
      // excited.
      if (logger.isInfoEnabled())
        logger.info("Failed to respond to an incoming " + "transactionless OPTIONS request");
      if (logger.isTraceEnabled()) logger.trace("Exception was:", ex);
      return false;
    } catch (InvalidArgumentException ex) {
      // What else could we do apart from logging?
      logger.warn("Failed to send an incoming OPTIONS request", ex);
      return false;
    } catch (SipException ex) {
      // What else could we do apart from logging?
      logger.warn("Failed to send an incoming OPTIONS request", ex);
      return false;
    }

    return true;
  }
예제 #8
0
  /**
   * Removes from the specified list of candidates providers connected to a registrar that does not
   * match the IP address that we are receiving a request from.
   *
   * @param candidates the list of providers we've like to filter.
   * @param request the request that we are currently dispatching
   */
  private void filterByAddress(List<ProtocolProviderServiceSipImpl> candidates, Request request) {
    Iterator<ProtocolProviderServiceSipImpl> iterPP = candidates.iterator();
    while (iterPP.hasNext()) {
      ProtocolProviderServiceSipImpl candidate = iterPP.next();

      if (candidate.getRegistrarConnection() == null) {
        // RegistrarLess connections are ok
        continue;
      }

      if (!candidate.getRegistrarConnection().isRegistrarless()
          && !candidate.getRegistrarConnection().isRequestFromSameConnection(request)) {
        iterPP.remove();
      }
    }
  }
예제 #9
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 processResponse(ResponseEvent event) {
    try {
      // we don't have to accept the transaction since we
      // created the request
      ClientTransaction transaction = event.getClientTransaction();
      if (logger.isTraceEnabled())
        logger.trace(
            "received response: "
                + event.getResponse().getStatusCode()
                + " "
                + event.getResponse().getReasonPhrase());

      if (transaction == null) {
        logger.warn("Transaction is null, probably already expired!");
        return;
      }

      ProtocolProviderServiceSipImpl service = getServiceData(transaction);
      if (service != null) {
        // Mark the dialog for the dispatching of later in-dialog
        // responses. If there is no dialog then the initial request
        // sure is marked otherwise we won't have found the service with
        // getServiceData(). The request has to be marked in case we
        // receive one more response in an out-of-dialog transaction.
        if (event.getDialog() != null) {
          SipApplicationData.setApplicationData(
              event.getDialog(), SipApplicationData.KEY_SERVICE, service);
        }
        service.processResponse(event);
      } else {
        logger.error(
            "We received a response which "
                + "wasn't marked, please report this to "
                + "*****@*****.**");
      }
    } 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);
    }
  }
예제 #10
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 processDialogTerminated(DialogTerminatedEvent event) {
   try {
     ProtocolProviderServiceSipImpl recipient =
         (ProtocolProviderServiceSipImpl)
             SipApplicationData.getApplicationData(
                 event.getDialog(), SipApplicationData.KEY_SERVICE);
     if (recipient == null) {
       logger.error(
           "Dialog wasn't marked, please report this to " + "*****@*****.**");
     } else {
       if (logger.isTraceEnabled()) logger.trace("service was found with dialog data");
       recipient.processDialogTerminated(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);
   }
 }
예제 #11
0
  /**
   * Listens for network changes and if we have a down interface and we have a tcp/tls provider
   * which is staying for 20 seconds in unregistering state, it cannot unregister cause its using
   * the old address which is currently down, and we must recreate its listening points so it can
   * further reconnect.
   *
   * @param event the change event.
   */
  public void configurationChanged(ChangeEvent event) {
    if (event.isInitial()) return;

    if (event.getType() == ChangeEvent.ADDRESS_DOWN) {
      for (final ProtocolProviderServiceSipImpl pp : listeners) {
        if (pp.getRegistrarConnection().getTransport() != null
            && (pp.getRegistrarConnection().getTransport().equals(ListeningPoint.TCP)
                || pp.getRegistrarConnection().getTransport().equals(ListeningPoint.TLS))) {
          ResetListeningPoint reseter;
          synchronized (resetListeningPointsTimers) {
            // we do this only once for transport
            if (resetListeningPointsTimers.containsKey(pp.getRegistrarConnection().getTransport()))
              continue;

            reseter = new ResetListeningPoint(pp);
            resetListeningPointsTimers.put(pp.getRegistrarConnection().getTransport(), reseter);
          }
          pp.addRegistrationStateChangeListener(reseter);
        }
      }
    }
  }
예제 #12
0
 /** Frees allocated resources. */
 void shutdown() {
   provider.removeRegistrationStateChangeListener(registrationListener);
 }
  /**
   * 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;
  }
예제 #14
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;
  }
예제 #15
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;
    }
  }