From 5bb302346139d72b27bb404cc49e876829881d2c Mon Sep 17 00:00:00 2001 From: Emil Ivov Date: Thu, 23 Mar 2006 22:59:13 +0000 Subject: [PATCH] implemented support for instant messaging in icq (testing is still pending though) --- ...rationSetBasicInstantMessagingIcqImpl.java | 400 ++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java 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 + } + + } +}