Example #1
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;
  }
  /**
   * Initialize (a new) Whiteboard with contact
   *
   * @param contact Contact used to init whiteboard
   */
  public void initWhiteboard(final Contact contact) {
    opSetWb =
        (OperationSetWhiteboarding)
            contact.getProtocolProvider().getOperationSet(OperationSetWhiteboarding.class);

    if (opSetWb == null) {
      logger.info("Contact does not support whiteboarding");
      return;
    }

    WhiteboardFrame wbf = getWhiteboardFrame(contact);
    if (wbf != null) {
      wbf.setVisible(true);
      return;
    }

    new Thread() {
      public void run() {
        try {
          WhiteboardSession wbSession =
              opSetWb.createWhiteboardSession(contact.getDisplayName(), null);

          WhiteboardFrame wbFrame = new WhiteboardFrame(WhiteboardSessionManager.this, wbSession);

          wbFrames.add(wbFrame);
          wbFrame.setContact(contact);
          wbFrame.setVisible(true);

          wbSession.join();

          wbSession.invite(contact.getAddress());

        } catch (OperationFailedException e) {
          logger.error("Creating a whiteboard session failed.", e);
        } catch (OperationNotSupportedException e) {
          logger.error("Do not support create of whiteboard session", e);
        }
      }
    }.start();
  }
  /**
   * 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;
  }
  /**
   * The method would add a subscription for a contact, wait for a subscription event confirming the
   * subscription, then change the status of the newly added contact (which is actually the
   * IcqTesterAgent) and make sure that the corresponding notification events have been generated.
   *
   * @throws java.lang.Exception if an exception occurs during testing.
   */
  public void postTestSubscribe() throws Exception {
    logger.debug("Testing Subscription and Subscription Event Dispatch.");

    // First create a subscription and verify that it really gets created.
    SubscriptionEventCollector subEvtCollector = new SubscriptionEventCollector();

    logger.trace("set Auth Handler");
    operationSetPresence.setAuthorizationHandler(authEventCollector);

    synchronized (authEventCollector) {
      authEventCollector.authorizationRequestReason = "Please deny my request!";
      fixture.testerAgent.getAuthCmdFactory().responseReasonStr =
          "First authorization I will Deny!!!";
      fixture.testerAgent.getAuthCmdFactory().ACCEPT = false;
      operationSetPresence.subscribe(fixture.testerAgent.getIcqUIN());

      // this one collects event that the buddy has been added
      // to the list as awaiting
      SubscriptionEventCollector moveEvtCollector = new SubscriptionEventCollector();
      operationSetPresence.addSubscriptionListener(moveEvtCollector);

      logger.debug("Waiting for authorization error and authorization response...");
      authEventCollector.waitForAuthResponse(15000);
      assertTrue(
          "Error adding buddy not recieved or the buddy("
              + fixture.testerAgent.getIcqUIN()
              + ") doesn't require authorization",
          authEventCollector.isAuthorizationRequestSent);

      assertNotNull(
          "Agent haven't received any reason for authorization",
          fixture.testerAgent.getAuthCmdFactory().requestReasonStr);
      assertEquals(
          "Error sent request reason is not as the received one",
          authEventCollector.authorizationRequestReason,
          fixture.testerAgent.getAuthCmdFactory().requestReasonStr);

      logger.debug(
          "authEventCollector.isAuthorizationResponseReceived "
              + authEventCollector.isAuthorizationResponseReceived);

      assertTrue("Response not received!", authEventCollector.isAuthorizationResponseReceived);

      boolean isAcceptedAuthReuest =
          authEventCollector.response.getResponseCode().equals(AuthorizationResponse.ACCEPT);
      assertEquals(
          "Response is not as the sent one",
          fixture.testerAgent.getAuthCmdFactory().ACCEPT,
          isAcceptedAuthReuest);
      assertNotNull(
          "We didn't receive any reason! ", authEventCollector.authorizationResponseString);

      assertEquals(
          "The sent response reason is not as the received one",
          fixture.testerAgent.getAuthCmdFactory().responseReasonStr,
          authEventCollector.authorizationResponseString);

      // here we must wait for server to move the awaiting buddy
      // to the first specified  group
      synchronized (moveEvtCollector) {
        moveEvtCollector.waitForEvent(20000);
        // don't want any more events
        operationSetPresence.removeSubscriptionListener(moveEvtCollector);
      }

      Contact c = operationSetPresence.findContactByID(fixture.testerAgent.getIcqUIN());
      logger.debug("I will remove " + c + " from group : " + c.getParentContactGroup());

      UnsubscribeWait unsubscribeEvtCollector = new UnsubscribeWait();
      operationSetPresence.addSubscriptionListener(unsubscribeEvtCollector);

      synchronized (unsubscribeEvtCollector) {
        operationSetPresence.unsubscribe(c);
        logger.debug("Waiting to be removed...");
        unsubscribeEvtCollector.waitForUnsubscribre(20000);

        logger.debug("Received unsubscribed ok or we lost patients!");

        // don't want any more events
        operationSetPresence.removeSubscriptionListener(unsubscribeEvtCollector);
      }

      // so we haven't asserted so everithing is fine lets try to be authorized
      authEventCollector.authorizationRequestReason = "Please accept my request!";
      fixture.testerAgent.getAuthCmdFactory().responseReasonStr =
          "Second authorization I will Accept!!!";
      fixture.testerAgent.getAuthCmdFactory().ACCEPT = true;

      // clear some things
      authEventCollector.isAuthorizationRequestSent = false;
      authEventCollector.isAuthorizationResponseReceived = false;
      authEventCollector.authorizationResponseString = null;

      logger.debug(
          "I will add buddy does it exists ?  "
              + (operationSetPresence.findContactByID(fixture.testerAgent.getIcqUIN()) != null));
      // add the listener beacuse now our authorization will be accepted
      // and so the buddy will be finally added to the list
      operationSetPresence.addSubscriptionListener(subEvtCollector);
      // subscribe again so we can trigger again the authorization procedure
      operationSetPresence.subscribe(fixture.testerAgent.getIcqUIN());

      logger.debug(
          "Waiting ... Subscribe must fail and the authorization process "
              + "to be trigered again so waiting for auth response ...");
      authEventCollector.waitForAuthResponse(15000);

      assertTrue(
          "Error adding buddy not recieved or the buddy("
              + fixture.testerAgent.getIcqUIN()
              + ") doesn't require authorization",
          authEventCollector.isAuthorizationRequestSent);

      assertNotNull(
          "Agent haven't received any reason for authorization",
          fixture.testerAgent.getAuthCmdFactory().requestReasonStr);

      // not working for now
      assertEquals(
          "Error sent request reason",
          authEventCollector.authorizationRequestReason,
          fixture.testerAgent.getAuthCmdFactory().requestReasonStr);

      // wait for authorization process to be finnished
      // the modification of buddy (server will inform us
      // that he removed - awaiting authorization flag)
      Object obj = new Object();
      synchronized (obj) {
        logger.debug("wait for authorization process to be finnished");
        obj.wait(10000);
        logger.debug("Stop waiting!");
      }

      subEvtCollector.waitForEvent(10000);
      // don't want any more events
      operationSetPresence.removeSubscriptionListener(subEvtCollector);
    }

    // after adding awaitingAuthorization group here are catched 3 events
    // 1 - creating unresolved contact
    // 2 - move of the contact to awaitingAuthorization group
    // 3 - move of the contact from awaitingAuthorization group to original group
    assertTrue(
        "Subscription event dispatching failed.", subEvtCollector.collectedEvents.size() > 0);

    EventObject evt = null;

    Iterator<EventObject> events = subEvtCollector.collectedEvents.iterator();
    while (events.hasNext()) {
      EventObject elem = events.next();
      if (elem instanceof SubscriptionEvent) {
        if (((SubscriptionEvent) elem).getEventID() == SubscriptionEvent.SUBSCRIPTION_CREATED)
          evt = (SubscriptionEvent) elem;
      }
    }

    Object source = null;
    Contact srcContact = null;
    ProtocolProviderService srcProvider = null;

    // the event can be SubscriptionEvent and the new added one
    // SubscriptionMovedEvent

    if (evt instanceof SubscriptionEvent) {
      SubscriptionEvent subEvt = (SubscriptionEvent) evt;

      source = subEvt.getSource();
      srcContact = subEvt.getSourceContact();
      srcProvider = subEvt.getSourceProvider();
    }

    assertEquals(
        "SubscriptionEvent Source:",
        fixture.testerAgent.getIcqUIN(),
        ((Contact) source).getAddress());
    assertEquals(
        "SubscriptionEvent Source Contact:",
        fixture.testerAgent.getIcqUIN(),
        srcContact.getAddress());
    assertSame("SubscriptionEvent Source Provider:", fixture.provider, srcProvider);

    subEvtCollector.collectedEvents.clear();

    // make the user agent tester change its states and make sure we are
    // notified
    logger.debug("Testing presence notifications.");
    IcqStatusEnum testerAgentOldStatus = fixture.testerAgent.getPresneceStatus();
    IcqStatusEnum testerAgentNewStatus = IcqStatusEnum.FREE_FOR_CHAT;
    long testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_FFC;

    // in case we are by any chance already in a FREE_FOR_CHAT status, we'll
    // be changing to something else
    if (testerAgentOldStatus.equals(testerAgentNewStatus)) {
      testerAgentNewStatus = IcqStatusEnum.DO_NOT_DISTURB;
      testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_DND;
    }

    // now do the actual status notification testing
    ContactPresenceEventCollector contactPresEvtCollector =
        new ContactPresenceEventCollector(fixture.testerAgent.getIcqUIN(), testerAgentNewStatus);
    operationSetPresence.addContactPresenceStatusListener(contactPresEvtCollector);

    synchronized (contactPresEvtCollector) {
      if (!fixture.testerAgent.enterStatus(testerAgentNewStatusLong)) {
        throw new RuntimeException(
            "Tester UserAgent Failed to switch to the "
                + testerAgentNewStatus.getStatusName()
                + " state.");
      }
      // we may already have the event, but it won't hurt to check.
      contactPresEvtCollector.waitForEvent(12000);
      operationSetPresence.removeContactPresenceStatusListener(contactPresEvtCollector);
    }

    if (contactPresEvtCollector.collectedEvents.size() == 0) {
      logger.info(
          "PROBLEM. Authorisation process doesn't have finnished "
              + "Server doesn't report us for changing authorization flag! Will try to authorize once again");

      fixture.testerAgent.sendAuthorizationReplay(
          fixture.icqAccountID.getUserID(),
          fixture.testerAgent.getAuthCmdFactory().responseReasonStr,
          fixture.testerAgent.getAuthCmdFactory().ACCEPT);

      Object obj = new Object();
      synchronized (obj) {
        logger.debug("wait for authorization process to be finnished for second time");
        obj.wait(10000);
        logger.debug("Stop waiting!");
      }

      testerAgentOldStatus = fixture.testerAgent.getPresneceStatus();
      testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_FFC;

      // in case we are by any chance already in a FREE_FOR_CHAT status, we'll
      // be changing to something else
      if (testerAgentOldStatus.equals(testerAgentNewStatus)) {
        testerAgentNewStatus = IcqStatusEnum.OCCUPIED;
        testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_OCCUPIED;
      }

      contactPresEvtCollector =
          new ContactPresenceEventCollector(fixture.testerAgent.getIcqUIN(), testerAgentNewStatus);
      operationSetPresence.addContactPresenceStatusListener(contactPresEvtCollector);

      synchronized (contactPresEvtCollector) {
        if (!fixture.testerAgent.enterStatus(testerAgentNewStatusLong)) {
          throw new RuntimeException(
              "Tester UserAgent Failed to switch to the "
                  + testerAgentNewStatus.getStatusName()
                  + " state.");
        }
        // we may already have the event, but it won't hurt to check.
        contactPresEvtCollector.waitForEvent(12000);
        operationSetPresence.removeContactPresenceStatusListener(contactPresEvtCollector);
      }
    }

    assertEquals(
        "Presence Notif. event dispatching failed.",
        1,
        contactPresEvtCollector.collectedEvents.size());
    ContactPresenceStatusChangeEvent presEvt =
        (ContactPresenceStatusChangeEvent) contactPresEvtCollector.collectedEvents.get(0);

    assertEquals(
        "Presence Notif. event  Source:",
        fixture.testerAgent.getIcqUIN(),
        ((Contact) presEvt.getSource()).getAddress());
    assertEquals(
        "Presence Notif. event  Source Contact:",
        fixture.testerAgent.getIcqUIN(),
        presEvt.getSourceContact().getAddress());
    assertSame(
        "Presence Notif. event  Source Provider:", fixture.provider, presEvt.getSourceProvider());

    PresenceStatus reportedNewStatus = presEvt.getNewStatus();
    PresenceStatus reportedOldStatus = presEvt.getOldStatus();

    assertEquals("Reported new PresenceStatus: ", testerAgentNewStatus, reportedNewStatus);

    // don't require equality between the reported old PresenceStatus and
    // the actual presence status of the tester agent because a first
    // notification is not supposed to have the old status as it really was.
    assertNotNull("Reported old PresenceStatus: ", reportedOldStatus);

    /** @todo tester agent changes status message we see the new message */
    /** @todo we should see the alias of the tester agent. */
    Object obj = new Object();
    synchronized (obj) {
      logger.debug("wait a moment. give time to server");
      obj.wait(4000);
    }
  }