/**
   * A method that would simply send messages to a group of people so that they would get notified
   * that tests are being run.
   */
  public void testSendFunMessages() {
    String hostname = "";

    try {
      hostname = java.net.InetAddress.getLocalHost().getHostName() + ": ";
    } catch (UnknownHostException ex) {
    }

    String message =
        hostname
            + "Hello this is the SIP Communicator (version "
            + System.getProperty("sip-communicator.version")
            + ") build on: "
            + new Date().toString()
            + ". Have a very nice day!";

    String list = System.getProperty("accounts.reporting.ICQ_REPORT_LIST");

    logger.debug("Will send message " + message + " to: " + list);

    // if no property is specified - return
    if (list == null || list.trim().length() == 0) return;

    StringTokenizer tokenizer = new StringTokenizer(list, " ");

    while (tokenizer.hasMoreTokens()) {
      fixture.testerAgent.sendMessage(tokenizer.nextToken(), message);
    }
  }
  /**
   * Sends a file transfer request to the given <tt>toContact</tt> by specifying the local and
   * remote file path and the <tt>fromContact</tt>, sending the file.
   *
   * @param toContact the contact that should receive the file
   * @param file the file to send
   * @return the transfer object
   * @throws IllegalStateException if the protocol provider is not registered or connected
   * @throws IllegalArgumentException if some of the arguments doesn't fit the protocol requirements
   */
  public FileTransfer sendFile(Contact toContact, File file)
      throws IllegalStateException, IllegalArgumentException {
    assertConnected();

    if (file.length() > getMaximumFileLength())
      throw new IllegalArgumentException("File length exceeds the allowed one for this protocol");

    // Get the aim connection
    AimConnection aimConnection = icqProvider.getAimConnection();

    // Create an outgoing file transfer instance
    OutgoingFileTransfer outgoingFileTransfer =
        aimConnection
            .getIcbmService()
            .getRvConnectionManager()
            .createOutgoingFileTransfer(new Screenname(toContact.getAddress()));

    String id =
        String.valueOf(outgoingFileTransfer.getRvSessionInfo().getRvSession().getRvSessionId());

    FileTransferImpl outFileTransfer =
        new FileTransferImpl(outgoingFileTransfer, id, toContact, file, FileTransfer.OUT);

    // Adding the file to the outgoing file transfer
    try {
      outgoingFileTransfer.setSingleFile(new File(file.getPath()));
    } catch (IOException e) {
      if (logger.isDebugEnabled()) logger.debug("Error sending file", e);
      return null;
    }

    // Notify all interested listeners that a file transfer has been
    // created.
    FileTransferCreatedEvent event = new FileTransferCreatedEvent(outFileTransfer, new Date());

    fireFileTransferCreated(event);

    // Sending the file
    outgoingFileTransfer.sendRequest(new InvitationMessage(""));

    outFileTransfer.fireStatusChangeEvent(FileTransferStatusChangeEvent.PREPARING);

    return outFileTransfer;
  }
  /**
   * Function called when a icq file transfer request arrive
   *
   * @param manager the joustsim manager
   * @param transfer the incoming transfer
   */
  public void handleNewIncomingConnection(
      RvConnectionManager manager, IncomingRvConnection transfer) {
    if (transfer instanceof IncomingFileTransfer) {
      if (logger.isTraceEnabled())
        logger.trace("Incoming Icq file transfer request " + transfer.getClass());

      if (!(transfer instanceof IncomingFileTransfer)) {
        logger.warn("Wrong file transfer.");
        return;
      }

      OperationSetPersistentPresenceIcqImpl opSetPersPresence =
          (OperationSetPersistentPresenceIcqImpl)
              icqProvider.getOperationSet(OperationSetPersistentPresence.class);

      Contact sender =
          opSetPersPresence.findContactByID(transfer.getBuddyScreenname().getFormatted());

      IncomingFileTransfer incomingFileTransfer = (IncomingFileTransfer) transfer;

      final Date newDate = new Date();
      final IncomingFileTransferRequest req =
          new IncomingFileTransferRequestIcqImpl(
              icqProvider, this, incomingFileTransfer, sender, newDate);

      // this handels when we receive request and before accept or decline
      // it we receive cancel
      transfer.addEventListener(
          new RvConnectionEventListener() {
            public void handleEventWithStateChange(
                RvConnection transfer, RvConnectionState state, RvConnectionEvent event) {
              if (state == FileTransferState.FAILED && event instanceof BuddyCancelledEvent) {
                fireFileTransferRequestCanceled(
                    new FileTransferRequestEvent(
                        OperationSetFileTransferIcqImpl.this, req, newDate));
              }
            }

            public void handleEvent(RvConnection arg0, RvConnectionEvent arg1) {}
          });

      fireFileTransferRequest(new FileTransferRequestEvent(this, req, newDate));
    }
  }
/**
 * The ICQ protocol filetransfer OperationSet.
 *
 * @author Anthony Schmitt
 * @author Damian Minkov
 */
public class OperationSetFileTransferIcqImpl
    implements OperationSetFileTransfer, RvConnectionManagerListener {
  private static final Logger logger = Logger.getLogger(OperationSetFileTransferIcqImpl.class);

  /** A call back to the ICQ provider that created us. */
  private ProtocolProviderServiceIcqImpl icqProvider = null;

  /** A list of listeners registered for file transfer events. */
  private ArrayList<FileTransferListener> fileTransferListeners =
      new ArrayList<FileTransferListener>();

  /**
   * Create a new FileTransfer OperationSet over the specified Icq provider
   *
   * @param icqProvider ICQ protocol provider service
   */
  public OperationSetFileTransferIcqImpl(ProtocolProviderServiceIcqImpl icqProvider) {
    this.icqProvider = icqProvider;

    icqProvider.addRegistrationStateChangeListener(new RegistrationStateListener());
  }

  /**
   * Sends a file transfer request to the given <tt>toContact</tt> by specifying the local and
   * remote file path and the <tt>fromContact</tt>, sending the file.
   *
   * @param toContact the contact that should receive the file
   * @param file the file to send
   * @return the transfer object
   * @throws IllegalStateException if the protocol provider is not registered or connected
   * @throws IllegalArgumentException if some of the arguments doesn't fit the protocol requirements
   */
  public FileTransfer sendFile(Contact toContact, File file)
      throws IllegalStateException, IllegalArgumentException {
    assertConnected();

    if (file.length() > getMaximumFileLength())
      throw new IllegalArgumentException("File length exceeds the allowed one for this protocol");

    // Get the aim connection
    AimConnection aimConnection = icqProvider.getAimConnection();

    // Create an outgoing file transfer instance
    OutgoingFileTransfer outgoingFileTransfer =
        aimConnection
            .getIcbmService()
            .getRvConnectionManager()
            .createOutgoingFileTransfer(new Screenname(toContact.getAddress()));

    String id =
        String.valueOf(outgoingFileTransfer.getRvSessionInfo().getRvSession().getRvSessionId());

    FileTransferImpl outFileTransfer =
        new FileTransferImpl(outgoingFileTransfer, id, toContact, file, FileTransfer.OUT);

    // Adding the file to the outgoing file transfer
    try {
      outgoingFileTransfer.setSingleFile(new File(file.getPath()));
    } catch (IOException e) {
      if (logger.isDebugEnabled()) logger.debug("Error sending file", e);
      return null;
    }

    // Notify all interested listeners that a file transfer has been
    // created.
    FileTransferCreatedEvent event = new FileTransferCreatedEvent(outFileTransfer, new Date());

    fireFileTransferCreated(event);

    // Sending the file
    outgoingFileTransfer.sendRequest(new InvitationMessage(""));

    outFileTransfer.fireStatusChangeEvent(FileTransferStatusChangeEvent.PREPARING);

    return outFileTransfer;
  }

  /**
   * Sends a file transfer request to the given <tt>toContact</tt> by specifying the local and
   * remote file path and the <tt>fromContact</tt>, sending the file.
   *
   * @param toContact the contact that should receive the file
   * @param fromContact the contact sending the file
   * @param remotePath the remote file path
   * @param localPath the local file path
   * @return the transfer object
   * @throws IllegalStateException if the protocol provider is not registered or connected
   * @throws IllegalArgumentException if some of the arguments doesn't fit the protocol requirements
   */
  public FileTransfer sendFile(
      Contact toContact, Contact fromContact, String remotePath, String localPath)
      throws IllegalStateException, IllegalArgumentException {
    return this.sendFile(toContact, new File(localPath));
  }

  /**
   * Adds the given <tt>FileTransferListener</tt> that would listen for file transfer requests and
   * created file transfers.
   *
   * @param listener the <tt>FileTransferListener</tt> to add
   */
  public void addFileTransferListener(FileTransferListener listener) {
    synchronized (fileTransferListeners) {
      if (!fileTransferListeners.contains(listener)) {
        this.fileTransferListeners.add(listener);
      }
    }
  }

  /**
   * Removes the given <tt>FileTransferListener</tt> that listens for file transfer requests and
   * created file transfers.
   *
   * @param listener the <tt>FileTransferListener</tt> to remove
   */
  public void removeFileTransferListener(FileTransferListener listener) {
    synchronized (fileTransferListeners) {
      this.fileTransferListeners.remove(listener);
    }
  }

  /**
   * Utility method throwing an exception if the stack is not properly initialized.
   *
   * @throws java.lang.IllegalStateException if the underlying stack is not registered and
   *     initialized.
   */
  private void assertConnected() throws IllegalStateException {
    if (icqProvider == null)
      throw new IllegalStateException(
          "The provider must be non-null and signed on the "
              + "service before being able to send a file.");
    else if (!icqProvider.isRegistered())
      throw new IllegalStateException(
          "The provider must be signed on the service before " + "being able to send a file.");
  }

  /**
   * Function called when a icq file transfer request arrive
   *
   * @param manager the joustsim manager
   * @param transfer the incoming transfer
   */
  public void handleNewIncomingConnection(
      RvConnectionManager manager, IncomingRvConnection transfer) {
    if (transfer instanceof IncomingFileTransfer) {
      if (logger.isTraceEnabled())
        logger.trace("Incoming Icq file transfer request " + transfer.getClass());

      if (!(transfer instanceof IncomingFileTransfer)) {
        logger.warn("Wrong file transfer.");
        return;
      }

      OperationSetPersistentPresenceIcqImpl opSetPersPresence =
          (OperationSetPersistentPresenceIcqImpl)
              icqProvider.getOperationSet(OperationSetPersistentPresence.class);

      Contact sender =
          opSetPersPresence.findContactByID(transfer.getBuddyScreenname().getFormatted());

      IncomingFileTransfer incomingFileTransfer = (IncomingFileTransfer) transfer;

      final Date newDate = new Date();
      final IncomingFileTransferRequest req =
          new IncomingFileTransferRequestIcqImpl(
              icqProvider, this, incomingFileTransfer, sender, newDate);

      // this handels when we receive request and before accept or decline
      // it we receive cancel
      transfer.addEventListener(
          new RvConnectionEventListener() {
            public void handleEventWithStateChange(
                RvConnection transfer, RvConnectionState state, RvConnectionEvent event) {
              if (state == FileTransferState.FAILED && event instanceof BuddyCancelledEvent) {
                fireFileTransferRequestCanceled(
                    new FileTransferRequestEvent(
                        OperationSetFileTransferIcqImpl.this, req, newDate));
              }
            }

            public void handleEvent(RvConnection arg0, RvConnectionEvent arg1) {}
          });

      fireFileTransferRequest(new FileTransferRequestEvent(this, req, newDate));
    }
  }

  /**
   * Delivers the specified event to all registered file transfer listeners.
   *
   * @param event the <tt>EventObject</tt> that we'd like delivered to all registered file transfer
   *     listeners.
   */
  private void fireFileTransferRequest(FileTransferRequestEvent event) {
    Iterator<FileTransferListener> listeners = null;
    synchronized (fileTransferListeners) {
      listeners = new ArrayList<FileTransferListener>(fileTransferListeners).iterator();
    }

    while (listeners.hasNext()) {
      FileTransferListener listener = listeners.next();

      listener.fileTransferRequestReceived(event);
    }
  }

  /**
   * Delivers the specified event to all registered file transfer listeners.
   *
   * @param event the <tt>EventObject</tt> that we'd like delivered to all registered file transfer
   *     listeners.
   */
  void fireFileTransferRequestRejected(FileTransferRequestEvent event) {
    Iterator<FileTransferListener> listeners = null;
    synchronized (fileTransferListeners) {
      listeners = new ArrayList<FileTransferListener>(fileTransferListeners).iterator();
    }

    while (listeners.hasNext()) {
      FileTransferListener listener = listeners.next();

      listener.fileTransferRequestRejected(event);
    }
  }

  /**
   * Delivers the specified event to all registered file transfer listeners.
   *
   * @param event the <tt>EventObject</tt> that we'd like delivered to all registered file transfer
   *     listeners.
   */
  void fireFileTransferRequestCanceled(FileTransferRequestEvent event) {
    Iterator<FileTransferListener> listeners = null;
    synchronized (fileTransferListeners) {
      listeners = new ArrayList<FileTransferListener>(fileTransferListeners).iterator();
    }

    while (listeners.hasNext()) {
      FileTransferListener listener = listeners.next();

      listener.fileTransferRequestCanceled(event);
    }
  }

  /**
   * Delivers the file transfer to all registered listeners.
   *
   * @param event the <tt>FileTransferEvent</tt> that we'd like delivered to all registered file
   *     transfer listeners.
   */
  void fireFileTransferCreated(FileTransferCreatedEvent event) {
    Iterator<FileTransferListener> listeners = null;
    synchronized (fileTransferListeners) {
      listeners = new ArrayList<FileTransferListener>(fileTransferListeners).iterator();
    }

    while (listeners.hasNext()) {
      FileTransferListener listener = listeners.next();
      listener.fileTransferCreated(event);
    }
  }

  /**
   * Returns the maximum file length supported by the protocol in bytes. Supports up to 2GB.
   *
   * @return the file length that is supported.
   */
  public long getMaximumFileLength() {
    return 2147483648l; // = 2048*1024*1024;
  }

  /** Our listener that will tell us when we're registered to */
  private class RegistrationStateListener implements RegistrationStateChangeListener {
    /**
     * The method is called by a ProtocolProvider implementation whenever a change in the
     * registration state of the corresponding provider had occurred.
     *
     * @param evt ProviderStatusChangeEvent the event describing the status change.
     */
    public void registrationStateChanged(RegistrationStateChangeEvent evt) {
      if (logger.isDebugEnabled())
        logger.debug(
            "The provider changed state from: " + evt.getOldState() + " to: " + evt.getNewState());

      if (evt.getNewState() == RegistrationState.REGISTERED) {
        AimConnection aimConnection = icqProvider.getAimConnection();
        aimConnection
            .getIcbmService()
            .getRvConnectionManager()
            .addConnectionManagerListener(OperationSetFileTransferIcqImpl.this);
      }
    }
  }
}
/**
 * Performs testing of the basic instant messaging operation set. Tests include going over basic
 * functionality such as sending a message from the tested implementation and asserting reception by
 * the tester agent and vice versa.
 *
 * @author Emil Ivov
 */
public class TestOperationSetBasicInstantMessaging extends TestCase {
  private static final Logger logger =
      Logger.getLogger(TestOperationSetBasicInstantMessaging.class);

  private IcqSlickFixture fixture = new IcqSlickFixture();

  private OperationSetBasicInstantMessaging opSetBasicIM = null;
  private OperationSetPresence opSetPresence = null;

  public TestOperationSetBasicInstantMessaging(String name) {
    super(name);
  }

  /**
   * Get a reference to the basic IM operation set.
   *
   * @throws Exception if this is not a good day.
   */
  protected void setUp() throws Exception {
    super.setUp();
    fixture.setUp();

    Map supportedOperationSets = fixture.provider.getSupportedOperationSets();

    if (supportedOperationSets == null || supportedOperationSets.size() < 1)
      throw new NullPointerException(
          "No OperationSet implementations are supported by " + "this ICQ implementation. ");

    // get the operation set presence here.
    opSetBasicIM =
        (OperationSetBasicInstantMessaging)
            supportedOperationSets.get(OperationSetBasicInstantMessaging.class.getName());

    // if the op set is null then the implementation doesn't offer a typing.
    // operation set which is unacceptable for icq.
    if (opSetBasicIM == null) {
      throw new NullPointerException("No implementation for basic IM was found");
    }

    // we also need the presence op set in order to retrieve contacts.
    opSetPresence =
        (OperationSetPresence) supportedOperationSets.get(OperationSetPresence.class.getName());

    // if the op set is null show that we're not happy.
    if (opSetPresence == null) {
      throw new NullPointerException(
          "An implementation of the ICQ service must provide an "
              + "implementation of at least one of the PresenceOperationSets");
    }
  }

  protected void tearDown() throws Exception {
    super.tearDown();

    fixture.tearDown();
  }

  /**
   * Creates a test suite containing tests of this class in a specific order. We'll first execute
   * tests beginning with the "test" prefix and then go to ordered tests.We first execture tests for
   * receiving messagese, so that a volatile contact is created for the sender. we'll then be able
   * to retrieve this volatile contact and send them a message on our turn. We need to do things
   * this way as the contact corresponding to the tester agent has been removed in the previous test
   * and we no longer have it in our contact list.
   *
   * @return Test a testsuite containing all tests to execute.
   */
  public static Test suite() {
    TestSuite suite = new TestSuite(TestOperationSetBasicInstantMessaging.class);

    // the following 2 need to be run in the specified order.
    suite.addTest(new TestOperationSetBasicInstantMessaging("firstTestReceiveMessage"));
    suite.addTest(new TestOperationSetBasicInstantMessaging("thenTestSendMessage"));

    return suite;
  }

  /**
   * Send an instant message from the tested operation set and assert reception by the icq tester
   * agent.
   */
  public void firstTestReceiveMessage() {
    String body = "This is an IM coming from the tester agent" + " on " + new Date().toString();

    ImEventCollector evtCollector = new ImEventCollector();

    // add a msg listener and register to the op set and send an instant
    // msg from the tester agent.
    opSetBasicIM.addMessageListener(evtCollector);

    fixture.testerAgent.sendMessage(fixture.ourUserID, body);

    evtCollector.waitForEvent(10000);

    opSetBasicIM.removeMessageListener(evtCollector);

    // assert reception of a message event
    assertTrue(
        "No events delivered upon a received message", evtCollector.collectedEvents.size() > 0);

    // assert event instance of Message Received Evt
    assertTrue(
        "Received evt was not an instance of " + MessageReceivedEvent.class.getName(),
        evtCollector.collectedEvents.get(0) instanceof MessageReceivedEvent);

    // assert source contact == testAgent.uin
    MessageReceivedEvent evt = (MessageReceivedEvent) evtCollector.collectedEvents.get(0);
    assertEquals(
        "message sender ", evt.getSourceContact().getAddress(), fixture.testerAgent.getIcqUIN());

    // assert messageBody == body
    assertEquals("message body", body, evt.getSourceMessage().getContent());
  }

  /**
   * Send an instant message from the tester agent and assert reception by the tested implementation
   */
  public void thenTestSendMessage() {
    String body =
        "This is an IM coming from the tested implementation" + " on " + new Date().toString();

    // create the message
    net.java.sip.communicator.service.protocol.Message msg = opSetBasicIM.createMessage(body);

    // register a listener in the op set
    ImEventCollector imEvtCollector = new ImEventCollector();
    opSetBasicIM.addMessageListener(imEvtCollector);

    // register a listener in the tester agent
    JoustSimMessageEventCollector jsEvtCollector = new JoustSimMessageEventCollector();
    fixture.testerAgent.addConversationListener(fixture.ourUserID, jsEvtCollector);

    Contact testerAgentContact = opSetPresence.findContactByID(fixture.testerAgent.getIcqUIN());

    opSetBasicIM.sendInstantMessage(testerAgentContact, msg);

    imEvtCollector.waitForEvent(10000);
    jsEvtCollector.waitForEvent(10000);

    fixture.testerAgent.removeConversationListener(fixture.ourUserID, jsEvtCollector);
    opSetBasicIM.removeMessageListener(imEvtCollector);

    // verify that the message delivered event was dispatched
    assertTrue(
        "No events delivered when sending a message", imEvtCollector.collectedEvents.size() > 0);

    assertTrue(
        "Received evt was not an instance of " + MessageDeliveredEvent.class.getName(),
        imEvtCollector.collectedEvents.get(0) instanceof MessageDeliveredEvent);

    MessageDeliveredEvent evt = (MessageDeliveredEvent) imEvtCollector.collectedEvents.get(0);
    assertEquals(
        "message destination ",
        evt.getDestinationContact().getAddress(),
        fixture.testerAgent.getIcqUIN());

    assertSame("source message", msg, evt.getSourceMessage());

    // verify that the message has successfully arived at the destination
    assertTrue(
        "No messages received by the tester agent", jsEvtCollector.collectedMessageInfo.size() > 0);
    String receivedBody =
        ((MessageInfo) jsEvtCollector.collectedMessageInfo.get(0)).getMessage().getMessageBody();

    assertEquals("received message body", msg.getContent(), receivedBody);
  }

  /** Creates an Message through the simple createMessage() method and inspects its parameters. */
  public void testCreateMessage1() {
    String body =
        "This is an IM coming from the tested implementation" + " on " + new Date().toString();
    net.java.sip.communicator.service.protocol.Message msg = opSetBasicIM.createMessage(body);

    assertEquals("message body", body, msg.getContent());
    assertTrue("message body bytes", Arrays.equals(body.getBytes(), msg.getRawData()));
    assertEquals("message length", body.length(), msg.getSize());
    assertEquals(
        "message content type",
        OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE,
        msg.getContentType());

    assertEquals(
        "message encoding",
        OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING,
        msg.getEncoding());

    assertNotNull("message uid", msg.getMessageUID());

    // a further test on message uid.
    net.java.sip.communicator.service.protocol.Message msg2 = opSetBasicIM.createMessage(body);
    assertFalse("message uid", msg.getMessageUID().equals(msg2.getMessageUID()));
  }

  /** Creates an Message through the advance createMessage() method and inspects its parameters. */
  public void testCreateMessage2() {
    String body =
        "This is an IM coming from the tested implementation" + " on " + new Date().toString();
    String contentType = "text/html";
    String encoding = "UTF-16";
    String subject = "test message";
    net.java.sip.communicator.service.protocol.Message msg =
        opSetBasicIM.createMessage(body.getBytes(), contentType, encoding, subject);

    assertEquals("message body", body, msg.getContent());
    assertTrue("message body bytes", Arrays.equals(body.getBytes(), msg.getRawData()));
    assertEquals("message length", body.length(), msg.getSize());
    assertEquals("message content type", contentType, msg.getContentType());
    assertEquals("message encoding", encoding, msg.getEncoding());
    assertNotNull("message uid", msg.getMessageUID());

    // a further test on message uid.
    net.java.sip.communicator.service.protocol.Message msg2 = opSetBasicIM.createMessage(body);
    assertFalse("message uid", msg.getMessageUID().equals(msg2.getMessageUID()));
  }

  /** Collects instant messaging events. */
  private class ImEventCollector implements MessageListener {
    private List collectedEvents = new LinkedList();
    /**
     * Called when a new incoming <tt>Message</tt> has been received.
     *
     * @param evt the <tt>MessageReceivedEvent</tt> containing the newly received message, its
     *     sender and other details.
     */
    public void messageReceived(MessageReceivedEvent evt) {
      logger.debug("Received a MessageReceivedEvent: " + evt);

      synchronized (this) {
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Called to indicated that delivery of a message sent earlier has failed. Reason code and
     * phrase are contained by the <tt>MessageFailedEvent</tt>
     *
     * @param evt the <tt>MessageFailedEvent</tt> containing the ID of the message whose delivery
     *     has failed.
     */
    public void messageDeliveryFailed(MessageDeliveryFailedEvent evt) {
      logger.debug("Received a MessageDeliveryFailedEvent: " + evt);

      synchronized (this) {
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Called when the underlying implementation has received an indication that a message, sent
     * earlier has been successfully received by the destination.
     *
     * @param evt the MessageDeliveredEvent containing the id of the message that has caused the
     *     event.
     */
    public void messageDelivered(MessageDeliveredEvent evt) {
      logger.debug("Received a MessageDeliveredEvent: " + evt);

      synchronized (this) {
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Blocks until at least one event is received or until waitFor miliseconds pass (whichever
     * happens first).
     *
     * @param waitFor the number of miliseconds that we should be waiting for an event before simply
     *     bailing out.
     */
    public void waitForEvent(long waitFor) {
      synchronized (this) {
        if (collectedEvents.size() > 0) return;

        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a message evt", ex);
        }
      }
    }
  }

  /** The oscar.jar lib sends us typing events through this listener. */
  private class JoustSimMessageEventCollector implements ConversationListener {
    private List collectedMessageInfo = new LinkedList();

    /**
     * Adds <tt>minfo</tt> into the list of collected messages.
     *
     * @param c Conversation
     * @param minfo MessageInfo
     */
    public void gotMessage(Conversation c, MessageInfo minfo) {

      logger.debug("Message: [" + minfo.getMessage() + "] received from: " + c.getBuddy());
      synchronized (this) {
        collectedMessageInfo.add(minfo);
        notifyAll();
      }
    }

    /**
     * Blocks until at least one event is received or until waitFor miliseconds pass (whicever
     * happens first).
     *
     * @param waitFor the number of miliseconds that we should be waiting for an event before simply
     *     bailing out.
     */
    public void waitForEvent(long waitFor) {
      synchronized (this) {
        if (collectedMessageInfo.size() > 0) {
          logger.trace("evt already received. " + collectedMessageInfo);
          return;
        }

        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a subscription evt", ex);
        }
      }
    }

    // the follwoing methods only have dummy implementations here as they
    // do not interest us. complete implementatios are provider in the
    // basic instant messaging operation set.
    public void buddyInfoUpdated(IcbmService service, Screenname buddy, IcbmBuddyInfo info) {}

    public void conversationClosed(Conversation c) {}

    public void gotOtherEvent(Conversation conversation, ConversationEventInfo event) {}

    public void sentOtherEvent(Conversation conversation, ConversationEventInfo event) {}

    public void canSendMessageChanged(Conversation c, boolean canSend) {}

    public void conversationOpened(Conversation c) {}

    public void newConversation(IcbmService service, Conversation conv) {}

    public void sentMessage(Conversation c, MessageInfo minfo) {}
  }

  /**
   * A method that would simply send messages to a group of people so that they would get notified
   * that tests are being run.
   */
  public void testSendFunMessages() {
    String hostname = "";

    try {
      hostname = java.net.InetAddress.getLocalHost().getHostName() + ": ";
    } catch (UnknownHostException ex) {
    }

    String message =
        hostname
            + "Hello this is the SIP Communicator (version "
            + System.getProperty("sip-communicator.version")
            + ") build on: "
            + new Date().toString()
            + ". Have a very nice day!";

    String list = System.getProperty("accounts.reporting.ICQ_REPORT_LIST");

    logger.debug("Will send message " + message + " to: " + list);

    // if no property is specified - return
    if (list == null || list.trim().length() == 0) return;

    StringTokenizer tokenizer = new StringTokenizer(list, " ");

    while (tokenizer.hasMoreTokens()) {
      fixture.testerAgent.sendMessage(tokenizer.nextToken(), message);
    }
  }

  /** Tests whether there is a offline message received and whether is the one we have send */
  public void testReceiveOfflineMessages() {
    String messageText = fixture.offlineMsgCollector.getMessageText();

    Message receiveMessage = fixture.offlineMsgCollector.getReceivedMessage();

    assertNotNull("No Offline messages have been received", receiveMessage);
    assertEquals("message body", messageText, receiveMessage.getContent());
  }
}