diff --git a/src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java b/src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java
new file mode 100644
index 000000000..5d048158f
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java
@@ -0,0 +1,400 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.icq;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import java.util.*;
+import net.kano.joustsim.*;
+import net.java.sip.communicator.util.*;
+import net.kano.joustsim.oscar.oscar.service.icbm.IcbmListener;
+import net.kano.joustsim.oscar.oscar.service.icbm.IcbmService;
+import net.kano.joustsim.oscar.oscar.service.icbm.Conversation;
+import net.kano.joustsim.oscar.oscar.service.icbm.IcbmBuddyInfo;
+import net.kano.joustsim.oscar.oscar.service.icbm.ConversationListener;
+import net.kano.joustsim.oscar.oscar.service.icbm.MessageInfo;
+import net.kano.joustsim.oscar.oscar.service.icbm.ConversationEventInfo;
+import net.kano.joustsim.oscar.oscar.service.icbm.ImConversation;
+import net.kano.joustsim.oscar.oscar.service.icbm.TypingInfo;
+import net.kano.joustsim.oscar.oscar.service.icbm.MissedImInfo;
+import net.kano.joustsim.oscar.oscar.service.icbm.ImConversationListener;
+
+/**
+ * A straightforward implementation of the basic instant messaging operation
+ * set.
+ *
+ * @author Emil Ivov
+ */
+public class OperationSetBasicInstantMessagingIcqImpl
+ implements OperationSetBasicInstantMessaging
+{
+
+ private static final Logger logger =
+ Logger.getLogger(OperationSetBasicInstantMessagingIcqImpl.class);
+
+ /**
+ * A list of listeneres registered for message events.
+ */
+ private Vector messageListeners = new Vector();
+
+ /**
+ * Default encoding for outgoing messages.
+ */
+ public static final String DEFAULT_MIME_ENCODING = "UTF-8";
+
+ /**
+ * Default mime type for outgoing messages.
+ */
+ public static final String DEFAULT_MIME_TYPE = "text/plain";
+
+ /**
+ * The icq provider that created us.
+ */
+ private ProtocolProviderServiceIcqImpl icqProvider = null;
+
+ /**
+ * The registration listener that would get notified when the unerlying
+ * icq provider gets registered.
+ */
+ private RegistrationStateListener providerRegListener
+ = new RegistrationStateListener();
+
+ /**
+ * The listener that would receive instant messaging events from oscar.jar
+ */
+ private JoustSimIcbmListener joustSimIcbmListener
+ = new JoustSimIcbmListener();
+
+ /**
+ * The listener that would receive conversation events from oscar.jar
+ */
+ private JoustSimConversationListener joustSimConversationListener
+ = new JoustSimConversationListener();
+
+ /**
+ * A reference to the persistent presence operation set that we use
+ * to match incoming messages to Contacts and vice versa.
+ */
+ private OperationSetPersistentPresenceIcqImpl opSetPersPresence = null;
+
+ /**
+ * Creates an instance of this operation set.
+ * @param icqProvider a ref to the ProtocolProviderServiceIcqImpl
+ * that created us and that we'll use for retrieving the underlying aim
+ * connection.
+ */
+ OperationSetBasicInstantMessagingIcqImpl(
+ ProtocolProviderServiceIcqImpl icqProvider)
+ {
+ this.icqProvider = icqProvider;
+ icqProvider.addRegistrationStateChangeListener(providerRegListener);
+ }
+
+ /**
+ * Registeres a MessageListener with this operation set so that it gets
+ * notifications of successful message delivery, failure or reception of
+ * incoming messages..
+ *
+ * @param listener the MessageListener to register.
+ */
+ public void addMessageListener(MessageListener listener)
+ {
+ synchronized(messageListeners)
+ {
+ this.messageListeners.add(listener);
+ }
+ }
+
+ /**
+ * Unregisteres listener so that it won't receive any further
+ * notifications upon successful message delivery, failure or reception of
+ * incoming messages..
+ *
+ * @param listener the MessageListener to unregister.
+ */
+ public void removeMessageListener(MessageListener listener)
+ {
+ synchronized(messageListeners)
+ {
+ this.messageListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Create a Message instance for sending arbitrary MIME-encoding content.
+ *
+ * @param content content value
+ * @param contentType the MIME-type for content
+ * @param contentEncoding encoding used for content
+ * @param subject a String subject or null for now subject.
+ * @return the newly created message.
+ */
+ public Message createMessage(byte[] content, String contentType,
+ String contentEncoding, String subject)
+ {
+ return new MessageIcqImpl(new String(content), contentType
+ , contentEncoding, subject);
+ }
+
+ /**
+ * Create a Message instance for sending a simple text messages with
+ * default (text/plain) content type and encoding.
+ *
+ * @param messageText the string content of the message.
+ * @return Message the newly created message
+ */
+ public Message createMessage(String messageText)
+ {
+ return new MessageIcqImpl(messageText, DEFAULT_MIME_TYPE
+ , DEFAULT_MIME_ENCODING, null);
+ }
+
+ /**
+ * Sends the message to the destination indicated by the
+ * to contact.
+ *
+ * @param to the Contact to send message to
+ * @param message the Message to send.
+ * @throws java.lang.IllegalStateException if the underlying ICQ stack is
+ * not registered and initialized.
+ * @throws java.lang.IllegalArgumentException if to is not an
+ * instance of ContactIcqImpl.
+ */
+ public void sendInstantMessage(Contact to, Message message)
+ throws IllegalStateException, IllegalArgumentException
+ {
+ assertConnected();
+
+ ImConversation imConversation =
+ icqProvider.getAimConnection().getIcbmService().getImConversation(
+ new Screenname(to.getAddress()));
+
+ //do not add the conversation listener in here. we'll add it
+ //inside the icbm listener
+ }
+
+ /**
+ * Utility method throwing an exception if the icq stack is not properly
+ * initialized.
+ * @throws java.lang.IllegalStateException if the underlying ICQ stack is
+ * not registered and initialized.
+ */
+ private void assertConnected() throws IllegalStateException
+ {
+ if (icqProvider == null)
+ throw new IllegalStateException(
+ "The icq provider must be non-null and signed on the ICQ "
+ +"service before being able to communicate.");
+ if (!icqProvider.isRegistered())
+ throw new IllegalStateException(
+ "The icq provider must be signed on the ICQ service before "
+ +"being able to communicate.");
+ }
+
+ /**
+ * Our listener that will tell us when we're registered to icq and joust
+ * sim is ready to accept us as a listener.
+ */
+ private class RegistrationStateListener
+ implements RegistrationStateChangeListener
+ {
+ /**
+ * The method is called by a ProtocolProvider implementation whenver
+ * 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)
+ {
+ logger.debug("The ICQ provider changed state from: "
+ + evt.getOldState()
+ + " to: " + evt.getNewState());
+ if (evt.getNewState() == RegistrationState.REGISTERED)
+ {
+ System.out.println("adding a Bos Service Listener");
+ icqProvider.getAimConnection().getIcbmService()
+ .addIcbmListener(joustSimIcbmListener);
+
+ opSetPersPresence = (OperationSetPersistentPresenceIcqImpl)
+ icqProvider.getSupportedOperationSets()
+ .get(OperationSetPersistentPresence.class.getName());
+
+ }
+ }
+ }
+
+ /**
+ * Delivers the specified event to all registered message listeners.
+ * @param evt the EventObject that we'd like delivered to all
+ * registered message listerners.
+ */
+ private void fireMessageEvent(EventObject evt)
+ {
+ synchronized(messageListeners)
+ {
+ for (int i = 0; i < messageListeners.size(); i++)
+ {
+ MessageListener l = (MessageListener)messageListeners.get(i);
+
+ if (evt instanceof MessageDeliveredEvent )
+ {
+ l.messageDelivered((MessageDeliveredEvent)evt);
+ }
+ else if (evt instanceof MessageReceivedEvent)
+ {
+ l.messageReceived((MessageReceivedEvent) evt);
+ }
+ else if (evt instanceof MessageDeliveryFailedEvent)
+ {
+ l.messageDeliveryFailed((MessageDeliveryFailedEvent) evt);
+ }
+ }
+ }
+ }
+
+ /**
+ * The listener that would retrieve instant messaging events from oscar.jar.
+ */
+ private class JoustSimIcbmListener implements IcbmListener
+ {
+ /**
+ * Register our icbm listener so that we get notified when new
+ * conversations are cretaed and register ourselvers as listeners in
+ * them.
+ *
+ * @param service the IcbmService that is clling us
+ * @param conv the Conversation that has just been created.
+ */
+ public void newConversation(IcbmService service, Conversation conv)
+ {
+ conv.addConversationListener(joustSimConversationListener);
+ }
+
+ /**
+ * Currently Unused.
+ * @param service Currently Unused.
+ * @param buddy Currently Unused.
+ * @param info Currently Unused.
+ */
+ public void buddyInfoUpdated(IcbmService service, Screenname buddy,
+ IcbmBuddyInfo info)
+ {
+ System.out.println("buddy info pudated for " + buddy
+ + " new info is: " + info);
+ }
+
+ }
+
+ /**
+ * Joust SIM supports the notion of instant messaging conversations and
+ * all message events are delivered through this listener. Right now we
+ * won't burden ourselves with conversations and would simply deliver
+ * events as we get them. If we need conversation support we'll implement it
+ * some other day.
+ */
+ private class JoustSimConversationListener implements ImConversationListener
+ {
+ /**
+ * Create a corresponding message object and fire a
+ * MessageReceivedEvent.
+ */
+ public void gotMessage(Conversation c, MessageInfo minfo)
+ {
+ if(logger.isDebugEnabled())
+ logger.debug("Received from " + c.getBuddy() + " the message "
+ + minfo.getMessage().getMessageBody());
+
+ Message newMessage = createMessage(
+ minfo.getMessage().getMessageBody());
+
+ Contact sourceContact = opSetPersPresence.findContactByID(
+ c.getBuddy().getFormatted());
+ if(sourceContact == null)
+ {
+ /** @todo received a message from a unknown contact. implement */
+ System.out.println("@todo received a message from a "
+ +"unknown contact. implement ext support.");
+ }
+
+ MessageReceivedEvent msgReceivedEvt
+ = new MessageReceivedEvent(
+ newMessage, sourceContact , minfo.getTimestamp() );
+
+ fireMessageEvent(msgReceivedEvt);
+
+ //temporarily and uglity fire the sent event here.
+ /** @todo move elsewhaere */
+ MessageDeliveredEvent msgDeliveredEvt
+ = new MessageDeliveredEvent(
+ newMessage, sourceContact , minfo.getTimestamp() );
+
+ fireMessageEvent(msgDeliveredEvt);
+
+ }
+
+ public void sentOtherEvent(Conversation conversation,
+ ConversationEventInfo event)
+ {
+ /**@todo implement sentOtherEvent() */
+ System.out.println("@todo implement sentOtherEvent()");
+ }
+
+ public void canSendMessageChanged(Conversation c, boolean canSend)
+ {
+ /**@todo implement canSendMessageChanged() */
+ System.out.println("@todo implement canSendMessageChanged()");
+ }
+
+ /** This may be called without ever calling conversationOpened */
+ public void conversationClosed(Conversation c)
+ {
+ /**@todo implement conversationClosed() */
+ System.out.println("@todo implement conversationClosed()");
+ }
+
+ /** This may never be called */
+ public void conversationOpened(Conversation c)
+ {
+ /**@todo implement conversationOpened() */
+ System.out.println("@todo implement conversationOpened()");
+ }
+
+ public void gotOtherEvent(Conversation conversation,
+ ConversationEventInfo event)
+ {
+ /**@todo implement gotOtherEvent() */
+ System.out.println("@todo implement gotOtherEvent()");
+ }
+
+ /** This may be called after conversationClosed is called */
+ public void sentMessage(Conversation c, MessageInfo minfo)
+ {
+ /**@todo implement sentMessage() */
+ /**
+ * there's no id in this event and besides we have no message failed
+ * method so refiring an event here would be difficult.
+ *
+ * we'll deal with that some other day.
+ */
+ System.out.println("@todo implement sentMessage()");
+ }
+
+ public void missedMessages(ImConversation conv, MissedImInfo info)
+ {
+ /**@todo implement missedMessages() */
+ System.out.println("@todo implement missedMessages()");
+ }
+
+ public void gotTypingState(Conversation conversation,
+ TypingInfo typingInfo)
+ {
+ //typing events are handled in OperationSetTypingNotifications
+ }
+
+ }
+}