diff options
author | Damian Minkov <damencho@jitsi.org> | 2007-01-12 17:56:58 +0000 |
---|---|---|
committer | Damian Minkov <damencho@jitsi.org> | 2007-01-12 17:56:58 +0000 |
commit | 7c6ccbd03b54d59913b45202f06bf053fe730f61 (patch) | |
tree | 5c1fd10f7382d1b43f2c67029ec0cdbafd260172 /src/net/java/sip/communicator/impl/protocol/yahoo | |
parent | 44d48d13eebcb30fbfedd84424c04820aabebba7 (diff) | |
download | jitsi-7c6ccbd03b54d59913b45202f06bf053fe730f61.zip jitsi-7c6ccbd03b54d59913b45202f06bf053fe730f61.tar.gz jitsi-7c6ccbd03b54d59913b45202f06bf053fe730f61.tar.bz2 |
Yahoo Protocol Provider Implementation
Diffstat (limited to 'src/net/java/sip/communicator/impl/protocol/yahoo')
17 files changed, 4953 insertions, 0 deletions
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java new file mode 100644 index 0000000..a4d409d --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java @@ -0,0 +1,29 @@ +/* + * 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.yahoo; + +import net.java.sip.communicator.service.protocol.*; + +/** + * The Yahoo implementation of the service.protocol.ContactGroup interface. There + * are two types of groups possible here. <tt>RootContactGroupYahooImpl</tt> + * which is the root node of the ContactList itself and + * <tt>ContactGroupYahooImpl</tt> which represents standard groups. The + * reason for having those 2 is that generally, Yahoo groups may not contain + * subgroups. A contact list on the other hand may not directly contain buddies. + * + * + * The reason for having an abstract class is only - being able to esily + * recognize our own (Yahoo) contacts. + * @author Damian Minkov + */ +public abstract class AbstractContactGroupYahooImpl + implements ContactGroup +{ + + +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java new file mode 100644 index 0000000..6c55018 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java @@ -0,0 +1,473 @@ +/* + * 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.yahoo; + +import java.util.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; +import ymsg.network.*; + +/** + * The Yahoo implementation of the ContactGroup interface. Intances of this class + * (contrary to <tt>RootContactGroupYahooImpl</tt>) may only contain buddies + * and cannot have sub groups. Note that instances of this class only use the + * corresponding smack source group for reading their names and only + * initially fill their <tt>buddies</tt> <tt>java.util.List</tt> with + * the ContactYahooImpl objects corresponding to those contained in the source + * group at the moment it is being created. They would, however, never try to + * sync or update their contents ulteriorly. This would have to be done through + * the addContact()/removeContact() methods. + * The content of buddies is created on creating of the group and when the smack + * source group is changed. + * + * @author Damian Minkov + */ +public class ContactGroupYahooImpl + extends AbstractContactGroupYahooImpl +{ + private static final Logger logger = + Logger.getLogger(ContactGroupYahooImpl.class); + + private List buddies = new LinkedList(); + private boolean isResolved = false; + + /** + * The Yahoo Group corresponding to this contact group. + */ + private YahooGroup yahooGroup = null; + + /** + * a list that would always remain empty. We only use it so that we're able + * to extract empty iterators + */ + private List dummyGroupsList = new LinkedList(); + + private String tempId = null; + + private ServerStoredContactListYahooImpl ssclCallback = null; + + /** + * Creates an Yahoo group using the specified <tt>YahooGroup</tt> as + * a source. The newly created group will always return the name of the + * underlying RosterGroup and would thus automatically adapt to changes. + * It would, however, not receive or try to poll for modifications of the + * buddies it contains and would therefore have to be updated manually by + * ServerStoredContactListImpl update will only be done if source group + * is changed. + + * @param yahooGroup the Yahoo Group correspoinding to the group + * @param groupMembers the group members that we should add to the group. + * @param ssclCallback a callback to the server stored contact list + * we're creating. + * @param isResolved a boolean indicating whether or not the group has been + * resolved against the server. + */ + ContactGroupYahooImpl( + YahooGroup yahooGroup, + Vector groupMembers, + ServerStoredContactListYahooImpl ssclCallback, + boolean isResolved) + { + this.yahooGroup = yahooGroup; + this.isResolved = isResolved; + this.ssclCallback = ssclCallback; + + Iterator iter = groupMembers.iterator(); + while(iter.hasNext()) + { + addContact( + new ContactYahooImpl( + (YahooUser)iter.next(), + ssclCallback, true, true) ); + } + } + + ContactGroupYahooImpl( + String id, + ServerStoredContactListYahooImpl ssclCallback) + { + this.tempId = id; + this.isResolved = false; + this.ssclCallback = ssclCallback; + } + + + /** + * Returns the number of <tt>Contact</tt> members of this + * <tt>ContactGroup</tt> + * + * @return an int indicating the number of <tt>Contact</tt>s, + * members of this <tt>ContactGroup</tt>. + */ + public int countContacts() + { + return buddies.size(); + } + + /** + * Returns a reference to the root group which in Yahoo is the parent of + * any other group since the protocol does not support subgroups. + * @return a reference to the root group. + */ + public ContactGroup getParentContactGroup() + { + return ssclCallback.getRootGroup(); + } + + /** + * Adds the specified contact at the specified position. + * @param contact the new contact to add to this group + * @param index the position where the new contact should be added. + */ + void addContact(int index, ContactYahooImpl contact) + { + buddies.add(index, contact); + } + + /** + * Adds the specified contact to the end of this group. + * @param contact the new contact to add to this group + */ + void addContact(ContactYahooImpl contact) + { + addContact(countContacts(), contact); + } + + + /** + * Removes the specified contact from this contact group + * @param contact the contact to remove. + */ + void removeContact(ContactYahooImpl contact) + { + removeContact(buddies.indexOf(contact)); + } + + /** + * Removes the contact with the specified index. + * @param index the index of the cntact to remove + */ + void removeContact(int index) + { + buddies.remove(index); + } + + /** + * Removes all buddies in this group and reinsterts them as specified + * by the <tt>newOrder</tt> param. Contacts not contained in the + * newOrder list are left at the end of this group. + * + * @param newOrder a list containing all contacts in the order that is + * to be applied. + * + */ + void reorderContacts(List newOrder) + { + buddies.removeAll(newOrder); + buddies.addAll(0, newOrder); + } + + /** + * Returns an Iterator over all contacts, member of this + * <tt>ContactGroup</tt>. + * + * @return a java.util.Iterator over all contacts inside this + * <tt>ContactGroup</tt>. In case the group doesn't contain any + * memebers it will return an empty iterator. + */ + public Iterator contacts() + { + return buddies.iterator(); + } + + /** + * Returns the <tt>Contact</tt> with the specified index. + * + * @param index the index of the <tt>Contact</tt> to return. + * @return the <tt>Contact</tt> with the specified index. + */ + public Contact getContact(int index) + { + return (ContactYahooImpl) buddies.get(index); + } + + /** + * Returns the <tt>Contact</tt> with the specified address or + * identifier. + * @param id the addres or identifier of the <tt>Contact</tt> we are + * looking for. + * @return the <tt>Contact</tt> with the specified id or address. + */ + public Contact getContact(String id) + { + return this.findContact(id); + } + + /** + * Returns the name of this group. + * @return a String containing the name of this group. + */ + public String getGroupName() + { + if(isResolved) + return yahooGroup.getName(); + else + return tempId; + } + + /** + * Determines whether the group may contain subgroups or not. + * + * @return always false since only the root group may contain subgroups. + */ + public boolean canContainSubgroups() + { + return false; + } + + /** + * Returns the subgroup with the specified index (i.e. always null since + * this group may not contain subgroups). + * + * @param index the index of the <tt>ContactGroup</tt> to retrieve. + * @return always null + */ + public ContactGroup getGroup(int index) + { + return null; + } + + /** + * Returns the subgroup with the specified name. + * @param groupName the name of the <tt>ContactGroup</tt> to retrieve. + * @return the <tt>ContactGroup</tt> with the specified index. + */ + public ContactGroup getGroup(String groupName) + { + return null; + } + + /** + * Returns an empty iterator. Subgroups may only be present in the root + * group. + * + * @return an empty iterator + */ + public Iterator subgroups() + { + return dummyGroupsList.iterator(); + } + + /** + * Returns the number of subgroups contained by this group, which is + * always 0 since sub groups in the protocol may only be contained + * by the root group - <tt>RootContactGroupImpl</tt>. + * @return a 0 int. + */ + public int countSubgroups() + { + return 0; + } + + /** + * Returns a hash code value for the object, which is actually the hashcode + * value of the groupname. + * + * @return a hash code value for this ContactGroup. + */ + public int hashCode() + { + return getGroupName().hashCode(); + } + + /** + * Indicates whether some other object is "equal to" this group. + * + * @param obj the reference object with which to compare. + * @return <tt>true</tt> if this object is the same as the obj + * argument; <tt>false</tt> otherwise. + */ + public boolean equals(Object obj) + { + if( obj == this ) + return true; + + if (obj == null + || !(obj instanceof ContactGroupYahooImpl) ) + return false; + + if(!((ContactGroup)obj).getGroupName().equals(getGroupName())) + return false; + + //since Yahoo does not support having two groups with the same name + // at this point we could bravely state that the groups are the same + // and not bother to compare buddies. (gotta check that though) + return true; + } + + /** + * Returns the protocol provider that this group belongs to. + * @return a regerence to the ProtocolProviderService instance that this + * ContactGroup belongs to. + */ + public ProtocolProviderService getProtocolProvider() + { + return this.ssclCallback.getParentProvider(); + } + + /** + * Returns a string representation of this group, in the form + * YahooGroup.GroupName[size]{ buddy1.toString(), buddy2.toString(), ...}. + * @return a String representation of the object. + */ + public String toString() + { + StringBuffer buff = new StringBuffer("YahooGroup."); + buff.append(getGroupName()); + buff.append(", childContacts="+countContacts()+":["); + + Iterator contacts = contacts(); + while (contacts.hasNext()) + { + ContactYahooImpl contact = (ContactYahooImpl) contacts.next(); + buff.append(contact.toString()); + if(contacts.hasNext()) + buff.append(", "); + } + return buff.append("]").toString(); + } + + /** + * Returns the contact encapsulating with the spcieified name or + * null if no such contact was found. + * + * @param id the id for the contact we're looking for. + * @return the <tt>ContactYahooImpl</tt> corresponding to the specified + * screnname or null if no such contact existed. + */ + ContactYahooImpl findContact(String id) + { + Iterator contacts = contacts(); + while (contacts.hasNext()) + { + ContactYahooImpl item = (ContactYahooImpl) contacts.next(); + if(item.getAddress().equals(id)) + return item; + } + return null; + } + + /** + * Determines whether or not this contact group is being stored by the + * server. Non persistent contact groups exist for the sole purpose of + * containing non persistent contacts. + * @return true if the contact group is persistent and false otherwise. + */ + public boolean isPersistent() + { + return true; + } + + /** + * Returns null as no persistent data is required and the contact address is + * sufficient for restoring the contact. + * <p> + * @return null as no such data is needed. + */ + public String getPersistentData() + { + return null; + } + + /** + * Determines whether or not this contact group has been resolved against + * the server. Unresolved group are used when initially loading a contact + * list that has been stored in a local file until the presence operation + * set has managed to retrieve all the contact list from the server and has + * properly mapped contacts and groups to their corresponding on-line + * buddies. + * @return true if the contact has been resolved (mapped against a buddy) + * and false otherwise. + */ + public boolean isResolved() + { + return isResolved; + } + + /** + * Resolve this contact group against the specified group + * @param yahooGroup the server stored group + */ + void setResolved(YahooGroup yahooGroup) + { + if(isResolved) + return; + + this.isResolved = true; + + this.yahooGroup = yahooGroup; + + Vector contacts = yahooGroup.getMembers(); + Iterator iter = contacts.iterator(); + while(iter.hasNext()) + { + YahooUser item = (YahooUser)iter.next(); + + ContactYahooImpl contact = + ssclCallback.findContactById(item.getId()); + if(contact != null) + { + contact.setResolved(item); + + ssclCallback.fireContactResolved(this, contact); + } + else + { + ContactYahooImpl newContact = + new ContactYahooImpl(item, ssclCallback, true, true); + addContact(newContact); + + ssclCallback.fireContactAdded(this, newContact); + } + } + } + + /** + * Returns a <tt>String</tt> that uniquely represnets the group. In this we + * use the name of the group as an identifier. This may cause problems + * though, in clase the name is changed by some other application between + * consecutive runs of the sip-communicator. + * + * @return a String representing this group in a unique and persistent + * way. + */ + public String getUID() + { + return getGroupName(); + } + + /** + * The source group we are encapsulating + * @return YahooGroup + */ + YahooGroup getSourceGroup() + { + return yahooGroup; + } + + /** + * Change the source group + * change the buddies + * + * @param newGroup YahooGroup + */ + void setSourceGroup(YahooGroup newGroup) + { + this.yahooGroup = newGroup; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java new file mode 100644 index 0000000..cef5594 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java @@ -0,0 +1,278 @@ +/* + * 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.yahoo; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.yahooconstants.*; +import ymsg.network.*; + +/** + * The Yahoo implementation of the service.protocol.Contact interface. + * @author Damian Minkov + */ +public class ContactYahooImpl + implements Contact +{ + private YahooUser contact = null; + private boolean isLocal = false; + private byte[] image = null; + private PresenceStatus status = YahooStatusEnum.OFFLINE; + private ServerStoredContactListYahooImpl ssclCallback = null; + private boolean isPersistent = false; + private boolean isResolved = false; + + private String tempId = null; + + /** + * Creates an YahooContactImpl + * @param contact the contact object that we will be encapsulating. + * @param ssclCallback a reference to the ServerStoredContactListImpl + * instance that created us. + * @param isPersistent determines whether this contact is persistent or not. + * @param isResolved specifies whether the contact has been resolved against + * the server contact list + */ + ContactYahooImpl( + YahooUser contact, + ServerStoredContactListYahooImpl ssclCallback, + boolean isPersistent, + boolean isResolved) + { + this.contact = contact; + this.isLocal = isLocal; + this.ssclCallback = ssclCallback; + this.isPersistent = isPersistent; + this.isResolved = isResolved; + } + + ContactYahooImpl( + String id, + ServerStoredContactListYahooImpl ssclCallback, + boolean isPersistent) + { + this.tempId = id; + this.isLocal = isLocal; + this.ssclCallback = ssclCallback; + this.isPersistent = isPersistent; + this.isResolved = false; + } + + /** + * Returns the Yahoo Userid of this contact + * @return the Yahoo Userid of this contact + */ + public String getAddress() + { + if(isResolved) + return contact.getId(); + else + return tempId; + } + + /** + * Determines whether or not this Contact instance represents the user used + * by this protocol provider to connect to the service. + * + * @return true if this Contact represents us (the local user) and false + * otherwise. + */ + public boolean isLocal() + { + return isLocal; + } + + public byte[] getImage() + { + return image; + } + + /** + * Returns a hashCode for this contact. The returned hashcode is actually + * that of the Contact's Address + * @return the hashcode of this Contact + */ + public int hashCode() + { + return getAddress().hashCode(); + } + + /** + * Indicates whether some other object is "equal to" this one. + * <p> + * + * @param obj the reference object with which to compare. + * @return <tt>true</tt> if this object is the same as the obj + * argument; <tt>false</tt> otherwise. + */ + public boolean equals(Object obj) + { + if (obj == null + || !(obj instanceof ContactYahooImpl) + || !(((ContactYahooImpl)obj).getAddress().equals(getAddress()) + && ((ContactYahooImpl)obj).getProtocolProvider() + == getProtocolProvider())) + return false; + else + return true; + } + + /** + * Returns a string representation of this contact, containing most of its + * representative details. + * + * @return a string representation of this contact. + */ + public String toString() + { + StringBuffer buff = new StringBuffer("YahooContact[ id="); + buff.append(getAddress()).append("]"); + + return buff.toString(); + } + + /** + * Sets the status that this contact is currently in. The method is to + * only be called as a result of a status update received from the server. + * + * @param status the YahooStatusEnum that this contact is currently in. + */ + void updatePresenceStatus(PresenceStatus status) + { + this.status = status; + } + + /** + * Returns the status of the contact as per the last status update we've + * received for it. Note that this method is not to perform any network + * operations and will simply return the status received in the last + * status update message. If you want a reliable way of retrieving someone's + * status, you should use the <tt>queryContactStatus()</tt> method in + * <tt>OperationSetPresence</tt>. + * @return the PresenceStatus that we've received in the last status update + * pertaining to this contact. + */ + public PresenceStatus getPresenceStatus() + { + return status; + } + + /** + * Returns a String that could be used by any user interacting modules for + * referring to this contact. An alias is not necessarily unique but is + * often more human readable than an address (or id). + * @return a String that can be used for referring to this contact when + * interacting with the user. + */ + public String getDisplayName() + { + if(isResolved) + return getAddress(); + else + return tempId; + } + + /** + * Returns a reference to the contact group that this contact is currently + * a child of or null if the underlying protocol does not suppord persistent + * presence. + * @return a reference to the contact group that this contact is currently + * a child of or null if the underlying protocol does not suppord persistent + * presence. + */ + public ContactGroup getParentContactGroup() + { + return ssclCallback.findContactGroup(this); + } + + + /** + * Returns a reference to the protocol provider that created the contact. + * @return a refererence to an instance of the ProtocolProviderService + */ + public ProtocolProviderService getProtocolProvider() + { + return ssclCallback.getParentProvider(); + } + + /** + * Determines whether or not this contact is being stored by the server. + * Non persistent contacts are common in the case of simple, non-persistent + * presence operation sets. They could however also be seen in persistent + * presence operation sets when for example we have received an event + * from someone not on our contact list. Non persistent contacts are + * volatile even when coming from a persistent presence op. set. They would + * only exist until the application is closed and will not be there next + * time it is loaded. + * @return true if the contact is persistent and false otherwise. + */ + public boolean isPersistent() + { + return isPersistent; + } + + /** + * Specifies whether this contact is to be considered persistent or not. The + * method is to be used _only_ when a non-persistent contact has been added + * to the contact list and its encapsulated VolatileBuddy has been repalced + * with a standard buddy. + * @param persistent true if the buddy is to be considered persistent and + * false for volatile. + */ + void setPersistent(boolean persistent) + { + this.isPersistent = persistent; + } + + /** + * Resolve this contact against the given entry + * @param entry the server stored entry + */ + void setResolved(YahooUser entry) + { + if(isResolved) + return; + + this.isResolved = true; + contact = entry; + } + + /** + * Returns the persistent data + * @return the persistent data + */ + public String getPersistentData() + { + return null; + } + + /** + * Determines whether or not this contact has been resolved against the + * server. Unresolved contacts are used when initially loading a contact + * list that has been stored in a local file until the presence operation + * set has managed to retrieve all the contact list from the server and has + * properly mapped contacts to their on-line buddies. + * @return true if the contact has been resolved (mapped against a buddy) + * and false otherwise. + */ + public boolean isResolved() + { + return isResolved; + } + + public void setPersistentData(String persistentData) + { + } + + /** + * Get source contact + * @return YahooContact + */ + YahooUser getSourceContact() + { + return contact; + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java new file mode 100644 index 0000000..a84c5c8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java @@ -0,0 +1,147 @@ +/* + * 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.yahoo; + +import net.java.sip.communicator.service.protocol.*; + +/** + * A simple implementation of the <tt>Message</tt> interface. Right now the + * message only supports test contents and no binary data. + * + * @author Damian Minkov + */ +public class MessageYahooImpl + implements Message +{ + /** + * The content of this message. + */ + private String textContent = null; + + /** + * The content type of text. Right now only text content types (such as + * text/plain or text/html) are supported. + */ + private String contentType = null; + + /** + * The encoding under which the contennt of this message is encoded. + */ + private String contentEncoding = null; + + /** + * An String uniquely identifying this Message. + */ + private String messageUID = null; + + /** + * The subject of the message if any (may remain null). + */ + private String subject = null; + + /** + * Creates an instance of this Message with the specified parameters. + * + * @param content the text content of the message. + * @param contentType a MIME string indicating the content type of the + * <tt>content</tt> String. + * @param contentEncoding a MIME String indicating the content encoding of + * the <tt>content</tt> String. + * @param subject the subject of the message or null for empty. + */ + public MessageYahooImpl(String content, + String contentType, + String contentEncoding, + String subject) + { + this.textContent = content; + this.contentType = contentType; + this.contentEncoding = contentEncoding; + this.subject = subject; + + //generate the uid + this.messageUID = String.valueOf( System.currentTimeMillis()) + + String.valueOf(hashCode()); + + } + + /** + * Returns the content of this message if representable in text form or + * null if this message does not contain text data. + * + * @return a String containing the content of this message or null if + * the message does not contain data representable in text form. + */ + public String getContent() + { + return textContent; + } + + /** + * Returns the MIME type for the message content. + * + * @return a String containing the mime type of the message contant. + */ + public String getContentType() + { + return contentType; + } + + /** + * Returns the MIME content encoding of this message. + * + * @return a String indicating the MIME encoding of this message. + */ + public String getEncoding() + { + return contentEncoding; + } + + /** + * Returns a unique identifier of this message. + * + * @return a String that uniquely represents this message in the scope + * of this protocol. + */ + public String getMessageUID() + { + return messageUID; + } + + /** + * Get the raw/binary content of an instant message. + * + * @return a byte[] array containing message bytes. + */ + public byte[] getRawData() + { + return getContent().getBytes(); + } + + /** + * Returns the size of the content stored in this message. + * + * @return an int indicating the number of bytes that this message + * contains. + */ + public int getSize() + { + return getContent().length(); + } + + /** + * Returns the subject of this message or null if the message contains no + * subject. + * + * @return the subject of this message or null if the message contains + * no subject. + */ + public String getSubject() + { + return subject; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java new file mode 100644 index 0000000..170ce2c --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java @@ -0,0 +1,303 @@ +/* + * 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.yahoo; + +import java.io.*; +import java.util.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.protocol.yahooconstants.*; +import net.java.sip.communicator.util.*; + +import ymsg.network.*; +import ymsg.network.event.*; + +/** + * A straightforward implementation of the basic instant messaging operation + * set. + * + * @author Damian Minkov + */ +public class OperationSetBasicInstantMessagingYahooImpl + implements OperationSetBasicInstantMessaging +{ + private static final Logger logger = + Logger.getLogger(OperationSetBasicInstantMessagingYahooImpl.class); + + /** + * A list of listeneres registered for message events. + */ + private Vector messageListeners = new Vector(); + + /** + * The provider that created us. + */ + private ProtocolProviderServiceYahooImpl yahooProvider = null; + + /** + * A reference to the persistent presence operation set that we use + * to match incoming messages to <tt>Contact</tt>s and vice versa. + */ + private OperationSetPersistentPresenceYahooImpl opSetPersPresence = null; + + /** + * 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. + */ + OperationSetBasicInstantMessagingYahooImpl( + ProtocolProviderServiceYahooImpl provider) + { + this.yahooProvider = provider; + provider.addRegistrationStateChangeListener(new RegistrationStateListener()); + } + + /** + * 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 <tt>MessageListener</tt> to register. + */ + public void addMessageListener(MessageListener listener) + { + synchronized(messageListeners) + { + if(!messageListeners.contains(listener)) + { + this.messageListeners.add(listener); + } + } + } + + /** + * Unregisteres <tt>listener</tt> so that it won't receive any further + * notifications upon successful message delivery, failure or reception of + * incoming messages.. + * + * @param listener the <tt>MessageListener</tt> to unregister. + */ + public void removeMessageListener(MessageListener listener) + { + synchronized(messageListeners) + { + this.messageListeners.remove(listener); + } + } + + /** + * Determines wheter the protocol provider (or the protocol itself) support + * sending and receiving offline messages. Most often this method would + * return true for protocols that support offline messages and false for + * those that don't. It is however possible for a protocol to support these + * messages and yet have a particular account that does not (i.e. feature + * not enabled on the protocol server). In cases like this it is possible + * for this method to return true even when offline messaging is not + * supported, and then have the sendMessage method throw an + * OperationFailedException with code - OFFLINE_MESSAGES_NOT_SUPPORTED. + * + * @return <tt>true</tt> if the protocol supports offline messages and + * <tt>false</tt> otherwise. + */ + public boolean isOfflineMessagingSupported() + { + return true; + } + + /** + * Create a Message instance for sending arbitrary MIME-encoding content. + * + * @param content content value + * @param contentType the MIME-type for <tt>content</tt> + * @param contentEncoding encoding used for <tt>content</tt> + * @param subject a <tt>String</tt> subject or <tt>null</tt> for now subject. + * @return the newly created message. + */ + public Message createMessage(byte[] content, String contentType, + String contentEncoding, String subject) + { + return new MessageYahooImpl(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 MessageYahooImpl(messageText, DEFAULT_MIME_TYPE + , DEFAULT_MIME_ENCODING, null); + } + + /** + * Sends the <tt>message</tt> to the destination indicated by the + * <tt>to</tt> contact. + * + * @param to the <tt>Contact</tt> to send <tt>message</tt> to + * @param message the <tt>Message</tt> to send. + * @throws java.lang.IllegalStateException if the underlying stack is + * not registered and initialized. + * @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an + * instance of ContactImpl. + */ + public void sendInstantMessage(Contact to, Message message) + throws IllegalStateException, IllegalArgumentException + { + assertConnected(); + + try + { + yahooProvider.getYahooSession().sendMessage( + ((ContactYahooImpl) to).getSourceContact().getId(), + message.getContent()); + + MessageDeliveredEvent msgDeliveredEvt + = new MessageDeliveredEvent( + message, to, new Date()); + + fireMessageEvent(msgDeliveredEvt); + } + catch (IOException ex) + { + logger.info("Cannot Send Message! " + ex.getMessage()); + MessageDeliveryFailedEvent evt = + new MessageDeliveryFailedEvent( + message, + to, + MessageDeliveryFailedEvent.NETWORK_FAILURE, + new Date()); + fireMessageEvent(evt); + return; + } + } + + /** + * 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 (yahooProvider == null) + throw new IllegalStateException( + "The provider must be non-null and signed on the " + +"service before being able to communicate."); + if (!yahooProvider.isRegistered()) + throw new IllegalStateException( + "The provider must be signed on the service before " + +"being able to communicate."); + } + + /** + * Our listener that will tell us when we're registered to + */ + 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 provider changed state from: " + + evt.getOldState() + + " to: " + evt.getNewState()); + + if (evt.getNewState() == RegistrationState.REGISTERED) + { + opSetPersPresence = (OperationSetPersistentPresenceYahooImpl) + yahooProvider.getSupportedOperationSets() + .get(OperationSetPersistentPresence.class.getName()); + + yahooProvider.getYahooSession(). + addSessionListener(new YahooMessageListener()); + } + } + } + + /** + * Delivers the specified event to all registered message listeners. + * @param evt the <tt>EventObject</tt> that we'd like delivered to all + * registered message listerners. + */ + private void fireMessageEvent(EventObject evt) + { + Iterator listeners = null; + synchronized (messageListeners) + { + listeners = new ArrayList(messageListeners).iterator(); + } + + while (listeners.hasNext()) + { + MessageListener listener + = (MessageListener) listeners.next(); + + if (evt instanceof MessageDeliveredEvent) + { + listener.messageDelivered( (MessageDeliveredEvent) evt); + } + else if (evt instanceof MessageReceivedEvent) + { + listener.messageReceived( (MessageReceivedEvent) evt); + } + else if (evt instanceof MessageDeliveryFailedEvent) + { + listener.messageDeliveryFailed( + (MessageDeliveryFailedEvent) evt); + } + } + } + + private class YahooMessageListener + extends SessionAdapter + { + public void messageReceived(SessionEvent ev) + { + handleNewMessage(ev); + } + + public void offlineMessageReceived(SessionEvent ev) + { + handleNewMessage(ev); + } + + private void handleNewMessage(SessionEvent ev) + { + Message newMessage = createMessage(ev.getMessage()); + + Contact sourceContact = opSetPersPresence. + findContactByID(ev.getFrom()); + + if(sourceContact == null) + { + logger.debug("received a message from an unknown contact: " + + ev.getFrom()); + //create the volatile contact + sourceContact = opSetPersPresence + .createVolatileContact(ev.getFrom()); + } + + MessageReceivedEvent msgReceivedEvt + = new MessageReceivedEvent( + newMessage, sourceContact , new Date() ); + + fireMessageEvent(msgReceivedEvt); + } + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java new file mode 100644 index 0000000..71e6578 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java @@ -0,0 +1,1066 @@ +/* + * 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.yahoo; + +import java.beans.*; +import java.io.*; +import java.util.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.protocol.yahooconstants.*; +import net.java.sip.communicator.util.*; +import ymsg.network.*; +import ymsg.network.event.*; + +/** + * The Yahoo implementation of a Persistent Presence Operation set. This class + * manages our own presence status as well as subscriptions for the presence + * status of our buddies. It also offers methods for retrieving and modifying + * the buddy contact list and adding listeners for changes in its layout. + * + * @author Damian Minkov + */ +public class OperationSetPersistentPresenceYahooImpl + implements OperationSetPersistentPresence +{ + private static final Logger logger = + Logger.getLogger(OperationSetPersistentPresenceYahooImpl.class); + + /** + * A callback to the Yahoo provider that created us. + */ + private ProtocolProviderServiceYahooImpl yahooProvider = null; + + /** + * Contains our current status message. Note that this field would only + * be changed once the server has confirmed the new status message and + * not immediately upon setting a new one.. + */ + private String currentStatusMessage = ""; + + /** + * The presence status that we were last notified of etnering. + * The initial one is OFFLINE + */ + private PresenceStatus currentStatus = YahooStatusEnum.OFFLINE; + + /** + * The list of listeners interested in receiving changes in our local + * presencestatus. + */ + private Vector providerPresenceStatusListeners = new Vector(); + + /** + * The list of subscription listeners interested in receiving notifications + * whenever . + */ + private Vector subscriptionListeners = new Vector(); + + /** + * The list of presence status listeners interested in receiving presence + * notifications of changes in status of contacts in our contact list. + */ + private Vector contactPresenceStatusListeners = new Vector(); + + /** + * Sometimes status changes are received before the contact list is inited + * here we store such events so we can show them correctly + */ + private Hashtable earlyStatusChange = new Hashtable(); + + /** + * The array list we use when returning from the getSupportedStatusSet() + * method. + */ + private static final ArrayList supportedPresenceStatusSet = new ArrayList(); + static{ + supportedPresenceStatusSet.add(YahooStatusEnum.AVAILABLE); + supportedPresenceStatusSet.add(YahooStatusEnum.BE_RIGHT_BACK); + supportedPresenceStatusSet.add(YahooStatusEnum.BUSY); + supportedPresenceStatusSet.add(YahooStatusEnum.IDLE); + supportedPresenceStatusSet.add(YahooStatusEnum.INVISIBLE); + supportedPresenceStatusSet.add(YahooStatusEnum.NOT_AT_DESK); + supportedPresenceStatusSet.add(YahooStatusEnum.NOT_AT_HOME); + supportedPresenceStatusSet.add(YahooStatusEnum.NOT_IN_OFFICE); + supportedPresenceStatusSet.add(YahooStatusEnum.OFFLINE); + supportedPresenceStatusSet.add(YahooStatusEnum.ON_THE_PHONE); + supportedPresenceStatusSet.add(YahooStatusEnum.ON_VACATION); + supportedPresenceStatusSet.add(YahooStatusEnum.OUT_TO_LUNCH); + supportedPresenceStatusSet.add(YahooStatusEnum.STEPPED_OUT); + } + + /** + * A map containing bindings between SIP Communicator's yahoo presence status + * instances and Yahoo status codes + */ + private static Map scToYahooModesMappings = new Hashtable(); + static{ + scToYahooModesMappings.put(YahooStatusEnum.AVAILABLE, + new Long(StatusConstants.STATUS_AVAILABLE)); + scToYahooModesMappings.put(YahooStatusEnum.BE_RIGHT_BACK, + new Long(StatusConstants.STATUS_BRB)); + scToYahooModesMappings.put(YahooStatusEnum.BUSY, + new Long(StatusConstants.STATUS_BUSY)); + scToYahooModesMappings.put(YahooStatusEnum.IDLE, + new Long(StatusConstants.STATUS_IDLE)); + scToYahooModesMappings.put(YahooStatusEnum.INVISIBLE, + new Long(StatusConstants.STATUS_INVISIBLE)); + scToYahooModesMappings.put(YahooStatusEnum.NOT_AT_DESK, + new Long(StatusConstants.STATUS_NOTATDESK)); + scToYahooModesMappings.put(YahooStatusEnum.NOT_AT_HOME, + new Long(StatusConstants.STATUS_NOTATHOME)); + scToYahooModesMappings.put(YahooStatusEnum.NOT_IN_OFFICE, + new Long(StatusConstants.STATUS_NOTINOFFICE)); + scToYahooModesMappings.put(YahooStatusEnum.OFFLINE, + new Long(StatusConstants.STATUS_OFFLINE)); + scToYahooModesMappings.put(YahooStatusEnum.ON_THE_PHONE, + new Long(StatusConstants.STATUS_ONPHONE)); + scToYahooModesMappings.put(YahooStatusEnum.ON_VACATION, + new Long(StatusConstants.STATUS_ONVACATION)); + scToYahooModesMappings.put(YahooStatusEnum.OUT_TO_LUNCH, + new Long(StatusConstants.STATUS_OUTTOLUNCH)); + scToYahooModesMappings.put(YahooStatusEnum.STEPPED_OUT, + new Long(StatusConstants.STATUS_STEPPEDOUT)); + } + + /** + * The server stored contact list that will be encapsulating smack's + * buddy list. + */ + private ServerStoredContactListYahooImpl ssContactList = null; + + public OperationSetPersistentPresenceYahooImpl( + ProtocolProviderServiceYahooImpl provider) + { + this.yahooProvider = provider; + + ssContactList = new ServerStoredContactListYahooImpl( this , provider); + + this.yahooProvider.addRegistrationStateChangeListener( + new RegistrationStateListener()); + } + + /** + * Registers a listener that would receive a presence status change event + * every time a contact, whose status we're subscribed for, changes her + * status. + * + * @param listener the listener that would received presence status + * updates for contacts. + */ + public void addContactPresenceStatusListener(ContactPresenceStatusListener + listener) + { + synchronized(contactPresenceStatusListeners){ + this.contactPresenceStatusListeners.add(listener); + } + } + + /** + * Adds a listener that would receive events upon changes of the provider + * presence status. + * + * @param listener the listener to register for changes in our + * PresenceStatus. + */ + public void addProviderPresenceStatusListener( + ProviderPresenceStatusListener listener) + { + synchronized(providerPresenceStatusListeners){ + providerPresenceStatusListeners.add(listener); + } + } + + /** + * Registers a listener that would receive events upong changes in server + * stored groups. + * + * @param listener a ServerStoredGroupChangeListener impl that would + * receive events upong group changes. + */ + public void addServerStoredGroupChangeListener(ServerStoredGroupListener + listener) + { + ssContactList.addGroupListener(listener); + } + + /** + * Registers a listener that would get notifications any time a new + * subscription was succesfully added, has failed or was removed. + * + * @param listener the SubscriptionListener to register + */ + public void addSubsciptionListener(SubscriptionListener listener) + { + synchronized(subscriptionListeners){ + subscriptionListeners.add(listener); + } + } + + /** + * Creates a group with the specified name and parent in the server + * stored contact list. + * + * @param parent the group where the new group should be created + * @param groupName the name of the new group to create. + * @throws OperationFailedException if such group already exists + */ + public void createServerStoredContactGroup(ContactGroup parent, + String groupName) + throws OperationFailedException + { + assertConnected(); + + if (!parent.canContainSubgroups()) + throw new IllegalArgumentException( + "The specified contact group cannot contain child groups. Group:" + + parent ); + + ssContactList.createGroup(groupName); + } + + /** + * Creates a non persistent contact for the specified address. This would + * also create (if necessary) a group for volatile contacts that would not + * be added to the server stored contact list. The volatile contact would + * remain in the list until it is really added to the contact list or + * until the application is terminated. + * @param id the address of the contact to create. + * @return the newly created volatile <tt>ContactImpl</tt> + */ + public ContactYahooImpl createVolatileContact(String id) + { + return ssContactList.createVolatileContact(id); + } + + /** + * Creates and returns a unresolved contact from the specified + * <tt>address</tt> and <tt>persistentData</tt>. + * + * @param address an identifier of the contact that we'll be creating. + * @param persistentData a String returned Contact's getPersistentData() + * method during a previous run and that has been persistently stored + * locally. + * @param parentGroup the group where the unresolved contact is supposed + * to belong to. + * @return the unresolved <tt>Contact</tt> created from the specified + * <tt>address</tt> and <tt>persistentData</tt> + */ + public Contact createUnresolvedContact(String address, + String persistentData, + ContactGroup parentGroup) + { + if(! (parentGroup instanceof ContactGroupYahooImpl || + parentGroup instanceof RootContactGroupYahooImpl) ) + throw new IllegalArgumentException( + "Argument is not an yahoo contact group (group=" + + parentGroup + ")"); + + ContactYahooImpl contact = + ssContactList.createUnresolvedContact(parentGroup, address); + + contact.setPersistentData(persistentData); + + return contact; + } + + /** + * Creates and returns a unresolved contact from the specified + * <tt>address</tt> and <tt>persistentData</tt>. + * + * @param address an identifier of the contact that we'll be creating. + * @param persistentData a String returned Contact's getPersistentData() + * method during a previous run and that has been persistently stored + * locally. + * @return the unresolved <tt>Contact</tt> created from the specified + * <tt>address</tt> and <tt>persistentData</tt> + */ + public Contact createUnresolvedContact(String address, + String persistentData) + { + return createUnresolvedContact( address + , persistentData + , getServerStoredContactListRoot()); + } + + /** + * Creates and returns a unresolved contact group from the specified + * <tt>address</tt> and <tt>persistentData</tt>. + * + * @param groupUID an identifier, returned by ContactGroup's + * getGroupUID, that the protocol provider may use in order to create + * the group. + * @param persistentData a String returned ContactGroups's + * getPersistentData() method during a previous run and that has been + * persistently stored locally. + * @param parentGroup the group under which the new group is to be + * created or null if this is group directly underneath the root. + * @return the unresolved <tt>ContactGroup</tt> created from the + * specified <tt>uid</tt> and <tt>persistentData</tt> + */ + public ContactGroup createUnresolvedContactGroup(String groupUID, + String persistentData, ContactGroup parentGroup) + { + return ssContactList.createUnresolvedContactGroup(groupUID); + } + + /** + * Returns a reference to the contact with the specified ID in case we + * have a subscription for it and null otherwise/ + * + * @param contactID a String identifier of the contact which we're + * seeking a reference of. + * @return a reference to the Contact with the specified + * <tt>contactID</tt> or null if we don't have a subscription for the + * that identifier. + */ + public Contact findContactByID(String contactID) + { + return ssContactList.findContactById(contactID); + } + + /** + * Returns the status message that was confirmed by the serfver + * + * @return the last status message that we have requested and the aim + * server has confirmed. + */ + public String getCurrentStatusMessage() + { + return currentStatusMessage; + } + + /** + * Returns the protocol specific contact instance representing the local + * user. + * + * @return the Contact (address, phone number, or uin) that the Provider + * implementation is communicating on behalf of. + */ + public Contact getLocalContact() + { + return null; + } + + /** + * Returns a PresenceStatus instance representing the state this provider + * is currently in. + * + * @return the PresenceStatus last published by this provider. + */ + public PresenceStatus getPresenceStatus() + { + return currentStatus; + } + + /** + * Returns the root group of the server stored contact list. + * + * @return the root ContactGroup for the ContactList stored by this + * service. + */ + public ContactGroup getServerStoredContactListRoot() + { + return ssContactList.getRootGroup(); + } + + /** + * Returns the set of PresenceStatus objects that a user of this service + * may request the provider to enter. + * + * @return Iterator a PresenceStatus array containing "enterable" status + * instances. + */ + public Iterator getSupportedStatusSet() + { + return supportedPresenceStatusSet.iterator(); + } + + /** + * Removes the specified contact from its current parent and places it + * under <tt>newParent</tt>. + * + * @param contactToMove the <tt>Contact</tt> to move + * @param newParent the <tt>ContactGroup</tt> where <tt>Contact</tt> + * would be placed. + */ + public void moveContactToGroup(Contact contactToMove, + ContactGroup newParent) + { + assertConnected(); + + if( !(contactToMove instanceof ContactYahooImpl) ) + throw new IllegalArgumentException( + "The specified contact is not an yahoo contact." + contactToMove); + if( !(newParent instanceof ContactGroupYahooImpl) ) + throw new IllegalArgumentException( + "The specified group is not an yahoo contact group." + + newParent); + + ssContactList.moveContact((ContactYahooImpl)contactToMove, + (ContactGroupYahooImpl)newParent); + } + + /** + * Requests the provider to enter into a status corresponding to the + * specified paramters. + * + * @param status the PresenceStatus as returned by + * getRequestableStatusSet + * @param statusMessage the message that should be set as the reason to + * enter that status + * @throws IllegalArgumentException if the status requested is not a + * valid PresenceStatus supported by this provider. + * @throws IllegalStateException if the provider is not currently + * registered. + * @throws OperationFailedException with code NETWORK_FAILURE if + * publishing the status fails due to a network error. + */ + public void publishPresenceStatus(PresenceStatus status, + String statusMessage) throws + IllegalArgumentException, IllegalStateException, + OperationFailedException + { + assertConnected(); + + if (!(status instanceof YahooStatusEnum)) + throw new IllegalArgumentException( + status + " is not a valid Yahoo status"); + + if(status.equals(YahooStatusEnum.OFFLINE)) + { + yahooProvider.unregister(); + return; + } + + try + { + yahooProvider.getYahooSession().setStatus( + ((Long)scToYahooModesMappings.get(status)).longValue()); + + fireProviderPresenceStatusChangeEvent(currentStatus, status); + } + catch(IOException ex) + { + throw new OperationFailedException("Failed to set Status", + OperationFailedException.NETWORK_FAILURE); + } + } + + /** + * Get the PresenceStatus for a particular contact. + * + * @param contactIdentifier the identifier of the contact whose status + * we're interested in. + * @return PresenceStatus the <tt>PresenceStatus</tt> of the specified + * <tt>contact</tt> + * @throws IllegalArgumentException if <tt>contact</tt> is not a contact + * known to the underlying protocol provider + * @throws IllegalStateException if the underlying protocol provider is + * not registered/signed on a public service. + * @throws OperationFailedException with code NETWORK_FAILURE if + * retrieving the status fails due to errors experienced during + * network communication + */ + public PresenceStatus queryContactStatus(String contactIdentifier) throws + IllegalArgumentException, IllegalStateException, + OperationFailedException + { + + ContactYahooImpl contact = ssContactList.findContactById(contactIdentifier); + if(contact == null) + { + logger.info("Contact not found id :" + contactIdentifier); + return null; + } + else + return yahooStatusToPresenceStatus(contact.getSourceContact().getStatus()); + } + + /** + * Removes the specified listener so that it won't receive any further + * updates on contact presence status changes + * + * @param listener the listener to remove. + */ + public void removeContactPresenceStatusListener( + ContactPresenceStatusListener listener) + { + synchronized(contactPresenceStatusListeners){ + contactPresenceStatusListeners.remove(listener); + } + } + + /** + * Unregisters the specified listener so that it does not receive further + * events upon changes in local presence status. + * + * @param listener ProviderPresenceStatusListener + */ + public void removeProviderPresenceStatusListener( + ProviderPresenceStatusListener listener) + { + synchronized(providerPresenceStatusListeners){ + providerPresenceStatusListeners.remove(listener); + } + } + + /** + * Removes the specified group from the server stored contact list. + * + * @param group the group to remove. + */ + public void removeServerStoredContactGroup(ContactGroup group) + { + assertConnected(); + + if( !(group instanceof ContactGroupYahooImpl) ) + throw new IllegalArgumentException( + "The specified group is not an yahoo contact group: " + group); + + ssContactList.removeGroup(((ContactGroupYahooImpl)group)); + } + + /** + * Removes the specified group change listener so that it won't receive + * any further events. + * + * @param listener the ServerStoredGroupChangeListener to remove + */ + public void removeServerStoredGroupChangeListener(ServerStoredGroupListener + listener) + { + ssContactList.removeGroupListener(listener); + } + + /** + * Removes the specified subscription listener. + * + * @param listener the listener to remove. + */ + public void removeSubscriptionListener(SubscriptionListener listener) + { + synchronized(subscriptionListeners){ + subscriptionListeners.remove(listener); + } + } + + /** + * Renames the specified group from the server stored contact list. + * + * @param group the group to rename. + * @param newName the new name of the group. + */ + public void renameServerStoredContactGroup(ContactGroup group, + String newName) + { + assertConnected(); + + if( !(group instanceof ContactGroupYahooImpl) ) + throw new IllegalArgumentException( + "The specified group is not an yahoo contact group: " + group); + + throw new UnsupportedOperationException("Renaming group not supported!"); + //ssContactList.renameGroup((ContactGroupYahooImpl)group, newName); + } + + /** + * Handler for incoming authorization requests. + * + * @param handler an instance of an AuthorizationHandler for + * authorization requests coming from other users requesting + * permission add us to their contact list. + */ + public void setAuthorizationHandler(AuthorizationHandler handler) + { + ssContactList.setAuthorizationHandler(handler); + } + + /** + * Persistently adds a subscription for the presence status of the + * contact corresponding to the specified contactIdentifier and indicates + * that it should be added to the specified group of the server stored + * contact list. + * + * @param parent the parent group of the server stored contact list + * where the contact should be added. <p> + * @param contactIdentifier the contact whose status updates we are + * subscribing for. + * @throws IllegalArgumentException if <tt>contact</tt> or + * <tt>parent</tt> are not a contact known to the underlying protocol + * provider. + * @throws IllegalStateException if the underlying protocol provider is + * not registered/signed on a public service. + * @throws OperationFailedException with code NETWORK_FAILURE if + * subscribing fails due to errors experienced during network + * communication + */ + public void subscribe(ContactGroup parent, String contactIdentifier) throws + IllegalArgumentException, IllegalStateException, + OperationFailedException + { + assertConnected(); + + if(! (parent instanceof ContactGroupYahooImpl) ) + throw new IllegalArgumentException( + "Argument is not an yahoo contact group (group=" + parent + ")"); + + ssContactList.addContact((ContactGroupYahooImpl)parent, contactIdentifier); + } + + /** + * Adds a subscription for the presence status of the contact + * corresponding to the specified contactIdentifier. + * + * @param contactIdentifier the identifier of the contact whose status + * updates we are subscribing for. <p> + * @throws IllegalArgumentException if <tt>contact</tt> is not a contact + * known to the underlying protocol provider + * @throws IllegalStateException if the underlying protocol provider is + * not registered/signed on a public service. + * @throws OperationFailedException with code NETWORK_FAILURE if + * subscribing fails due to errors experienced during network + * communication + */ + public void subscribe(String contactIdentifier) throws + IllegalArgumentException, IllegalStateException, + OperationFailedException + { + assertConnected(); + + ssContactList.addContact(contactIdentifier); + } + + /** + * Removes a subscription for the presence status of the specified + * contact. + * + * @param contact the contact whose status updates we are unsubscribing + * from. + * @throws IllegalArgumentException if <tt>contact</tt> is not a contact + * known to the underlying protocol provider + * @throws IllegalStateException if the underlying protocol provider is + * not registered/signed on a public service. + * @throws OperationFailedException with code NETWORK_FAILURE if + * unsubscribing fails due to errors experienced during network + * communication + */ + public void unsubscribe(Contact contact) throws IllegalArgumentException, + IllegalStateException, OperationFailedException + { + assertConnected(); + + if(! (contact instanceof ContactYahooImpl) ) + throw new IllegalArgumentException( + "Argument is not an yahoo contact (contact=" + contact + ")"); + + ssContactList.removeContact((ContactYahooImpl)contact); + } + + /** + * Converts the specified yahoo status to one of the status fields of the + * YahooStatusEnum class. + * + * @param status the yahoo Status + * @return a PresenceStatus instance representation of the yahoo Status + * parameter. The returned result is one of the YahooStatusEnum fields. + */ + YahooStatusEnum yahooStatusToPresenceStatus(long status) + { + if(status == StatusConstants.STATUS_AVAILABLE) + return YahooStatusEnum.AVAILABLE; + else if(status == StatusConstants.STATUS_BRB) + return YahooStatusEnum.BE_RIGHT_BACK; + else if(status == StatusConstants.STATUS_BUSY) + return YahooStatusEnum.BUSY; + else if(status == StatusConstants.STATUS_NOTATHOME) + return YahooStatusEnum.NOT_AT_HOME; + else if(status == StatusConstants.STATUS_NOTATDESK) + return YahooStatusEnum.NOT_AT_DESK; + else if(status == StatusConstants.STATUS_NOTINOFFICE) + return YahooStatusEnum.NOT_IN_OFFICE; + else if(status == StatusConstants.STATUS_ONPHONE) + return YahooStatusEnum.ON_THE_PHONE; + else if(status == StatusConstants.STATUS_ONVACATION) + return YahooStatusEnum.ON_VACATION; + else if(status == StatusConstants.STATUS_OUTTOLUNCH) + return YahooStatusEnum.OUT_TO_LUNCH; + else if(status == StatusConstants.STATUS_STEPPEDOUT) + return YahooStatusEnum.STEPPED_OUT; + else if(status == StatusConstants.STATUS_INVISIBLE) + return YahooStatusEnum.INVISIBLE; + else if(status == StatusConstants.STATUS_IDLE) + return YahooStatusEnum.IDLE; + else if(status == StatusConstants.STATUS_OFFLINE) + return YahooStatusEnum.OFFLINE; + // Yahoo supports custom statuses so if such is set just return available + else + return YahooStatusEnum.OFFLINE; + } + + /** + * 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 (yahooProvider == null) + throw new IllegalStateException( + "The provider must be non-null and signed on the yahoo " + +"service before being able to communicate."); + if (!yahooProvider.isRegistered()) + throw new IllegalStateException( + "The provider must be signed on the yahoo service before " + +"being able to communicate."); + } + + /** + * Notify all provider presence listeners of the corresponding event change + * @param oldStatus the status our stack had so far + * @param newStatus the status we have from now on + */ + void fireProviderPresenceStatusChangeEvent( + PresenceStatus oldStatus, PresenceStatus newStatus) + { + if(oldStatus.equals(newStatus)){ + logger.debug("Ignored prov stat. change evt. old==new = " + + oldStatus); + return; + } + + ProviderPresenceStatusChangeEvent evt = + new ProviderPresenceStatusChangeEvent( + yahooProvider, oldStatus, newStatus); + + currentStatus = newStatus; + + + logger.debug("Dispatching Provider Status Change. Listeners=" + + providerPresenceStatusListeners.size() + + " evt=" + evt); + + Iterator listeners = null; + synchronized (providerPresenceStatusListeners) + { + listeners = new ArrayList(providerPresenceStatusListeners).iterator(); + } + + while (listeners.hasNext()) + { + ProviderPresenceStatusListener listener + = (ProviderPresenceStatusListener) listeners.next(); + + listener.providerStatusChanged(evt); + } + } + + /** + * Notify all provider presence listeners that a new status message has + * been set. + * @param oldStatusMessage the status message our stack had so far + * @param newStatusMessage the status message we have from now on + */ + private void fireProviderStatusMessageChangeEvent( + String oldStatusMessage, String newStatusMessage) + { + + PropertyChangeEvent evt = new PropertyChangeEvent( + yahooProvider, ProviderPresenceStatusListener.STATUS_MESSAGE, + oldStatusMessage, newStatusMessage); + + logger.debug("Dispatching stat. msg change. Listeners=" + + providerPresenceStatusListeners.size() + + " evt=" + evt); + + Iterator listeners = null; + synchronized (providerPresenceStatusListeners) + { + listeners = new ArrayList(providerPresenceStatusListeners).iterator(); + } + + while (listeners.hasNext()) + { + ProviderPresenceStatusListener listener = + (ProviderPresenceStatusListener) listeners.next(); + + listener.providerStatusMessageChanged(evt); + } + } + + /** + * Statuses have been received durring login process + * so we will init them once we are logged in + */ + private void initContactStatuses() + { + YahooGroup[] groups = yahooProvider.getYahooSession().getGroups(); + + for (int i = 0; i < groups.length; i++) + { + YahooGroup item = groups[i]; + Iterator iter = item.getMembers().iterator(); + while(iter.hasNext()) + { + YahooUser user = (YahooUser)iter.next(); + + ContactYahooImpl sourceContact = + ssContactList.findContactById(user.getId()); + + if(sourceContact != null) + handleContactStatusChange(sourceContact, user.getStatus()); + } + } + } + + /** + * Our listener that will tell us when we're registered to server + * and 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 yahoo provider changed state from: " + + evt.getOldState() + + " to: " + evt.getNewState()); + + if(evt.getNewState() == RegistrationState.REGISTERED) + { + yahooProvider.getYahooSession(). + addSessionListener(new StatusChangedListener()); + + ssContactList.setYahooSession(yahooProvider.getYahooSession()); + + initContactStatuses(); + } + else if(evt.getNewState() == RegistrationState.UNREGISTERED + || evt.getNewState() == RegistrationState.AUTHENTICATION_FAILED + || evt.getNewState() == RegistrationState.CONNECTION_FAILED) + { + //since we are disconnected, we won't receive any further status + //updates so we need to change by ourselves our own status as + //well as set to offline all contacts in our contact list that + //were online + PresenceStatus oldStatus = currentStatus; + currentStatus = YahooStatusEnum.OFFLINE; + + fireProviderPresenceStatusChangeEvent(oldStatus, + currentStatus); + + //send event notifications saying that all our buddies are + //offline. The protocol does not implement top level buddies + //nor subgroups for top level groups so a simple nested loop + //would be enough. + Iterator groupsIter = + getServerStoredContactListRoot().subgroups(); + while(groupsIter.hasNext()) + { + ContactGroupYahooImpl group + = (ContactGroupYahooImpl)groupsIter.next(); + + Iterator contactsIter = group.contacts(); + + while(contactsIter.hasNext()) + { + ContactYahooImpl contact + = (ContactYahooImpl)contactsIter.next(); + + PresenceStatus oldContactStatus + = contact.getPresenceStatus(); + + if(!oldContactStatus.isOnline()) + continue; + + contact.updatePresenceStatus(YahooStatusEnum.OFFLINE); + + fireContactPresenceStatusChangeEvent( + contact + , contact.getParentContactGroup() + , oldContactStatus, YahooStatusEnum.OFFLINE); + } + } + } + } + } + + /** + * Notify all subscription listeners of the corresponding event. + * + * @param eventID the int ID of the event to dispatch + * @param sourceContact the ContactYahooImpl instance that this event is + * pertaining to. + * @param parentGroup the ContactGroupYahooImpl under which the corresponding + * subscription is located. + */ + void fireSubscriptionEvent( int eventID, + ContactYahooImpl sourceContact, + ContactGroup parentGroup) + { + SubscriptionEvent evt = + new SubscriptionEvent(sourceContact, yahooProvider, parentGroup, + eventID); + + logger.debug("Dispatching a Subscription Event to" + +subscriptionListeners.size() + " listeners. Evt="+evt); + + Iterator listeners = null; + synchronized (subscriptionListeners) + { + listeners = new ArrayList(subscriptionListeners).iterator(); + } + + while (listeners.hasNext()) + { + SubscriptionListener listener + = (SubscriptionListener) listeners.next(); + + if (evt.getEventID() == SubscriptionEvent.SUBSCRIPTION_CREATED) + listener.subscriptionCreated(evt); + else if (evt.getEventID() == SubscriptionEvent.SUBSCRIPTION_REMOVED) + listener.subscriptionRemoved(evt); + else if (evt.getEventID() == SubscriptionEvent.SUBSCRIPTION_FAILED) + listener.subscriptionFailed(evt); + } + } + + /** + * Notify all subscription listeners of the corresponding event. + * + * @param sourceContact the ContactYahooImpl instance that this event is + * pertaining to. + * @param oldParentGroup the group that was previously a parent of the + * source contact. + * @param newParentGroup the group under which the corresponding + * subscription is currently located. + */ + void fireSubscriptionMovedEvent( ContactYahooImpl sourceContact, + ContactGroup oldParentGroup, + ContactGroup newParentGroup) + { + SubscriptionMovedEvent evt = + new SubscriptionMovedEvent(sourceContact, yahooProvider + , oldParentGroup, newParentGroup); + + logger.debug("Dispatching a Subscription Event to" + +subscriptionListeners.size() + " listeners. Evt="+evt); + + Iterator listeners = null; + synchronized (subscriptionListeners) + { + listeners = new ArrayList(subscriptionListeners).iterator(); + } + + while (listeners.hasNext()) + { + SubscriptionListener listener + = (SubscriptionListener) listeners.next(); + + listener.subscriptionMoved(evt); + } + } + + /** + * Notify all contact presence listeners of the corresponding event change + * @param contact the contact that changed its status + * @param oldStatus the status that the specified contact had so far + * @param newStatus the status that the specified contact is currently in. + * @param parentGroup the group containing the contact which caused the event + */ + private void fireContactPresenceStatusChangeEvent( + Contact contact, + ContactGroup parentGroup, + PresenceStatus oldStatus, + PresenceStatus newStatus) + { + ContactPresenceStatusChangeEvent evt = + new ContactPresenceStatusChangeEvent( + contact, yahooProvider, parentGroup, oldStatus, newStatus); + + + logger.debug("Dispatching Contact Status Change. Listeners=" + + contactPresenceStatusListeners.size() + + " evt=" + evt); + + Iterator listeners = null; + synchronized (contactPresenceStatusListeners) + { + listeners = new ArrayList(contactPresenceStatusListeners).iterator(); + } + + while (listeners.hasNext()) + { + ContactPresenceStatusListener listener + = (ContactPresenceStatusListener) listeners.next(); + + listener.contactPresenceStatusChanged(evt); + } + } + + void handleContactStatusChange(ContactYahooImpl sourceContact, long newStat) + { + PresenceStatus oldStatus + = sourceContact.getPresenceStatus(); + + PresenceStatus newStatus = yahooStatusToPresenceStatus(newStat); + + // when old and new status are the same do nothing - no change + if(oldStatus.equals(newStatus)) + return; + + sourceContact.updatePresenceStatus(newStatus); + + ContactGroup parent + = ssContactList.findContactGroup(sourceContact); + + logger.debug("Will Dispatch the contact status event."); + fireContactPresenceStatusChangeEvent(sourceContact, parent, + oldStatus, newStatus); + } + + private class StatusChangedListener + extends SessionAdapter + { + public void friendsUpdateReceived(SessionFriendEvent evt) + { + logger.debug("Received a status update for contact " + evt); + + ContactYahooImpl sourceContact = + ssContactList.findContactById(evt.getFriend().getId()); + + if(sourceContact == null) + { + if(yahooProvider.getAccountID().getUserID(). + equals(evt.getFriend().getId())) + { + // thats my own status + logger.trace("Own status changed to " + evt.getFriend().getStatus()); + PresenceStatus oldStatus = currentStatus; + currentStatus = + yahooStatusToPresenceStatus(evt.getFriend().getStatus()); + fireProviderPresenceStatusChangeEvent(oldStatus, currentStatus); + + return; + } + // strange + else + return; + } + + handleContactStatusChange(sourceContact, evt.getFriend().getStatus()); + } + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java new file mode 100644 index 0000000..6e7f4c4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java @@ -0,0 +1,219 @@ +/* + * 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.yahoo; + +import java.util.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; +import ymsg.network.event.*; + +/** + * Maps SIP Communicator typing notifications to those going and coming from + * smack lib. + * + * @author Damian Minkov + */ +public class OperationSetTypingNotificationsYahooImpl + implements OperationSetTypingNotifications +{ + private static final Logger logger = + Logger.getLogger(OperationSetTypingNotificationsYahooImpl.class); + + /** + * All currently registered TN listeners. + */ + private List typingNotificationsListeners = new ArrayList(); + + /** + * The provider that created us. + */ + private ProtocolProviderServiceYahooImpl yahooProvider = null; + + /** + * An active instance of the opSetPersPresence operation set. We're using + * it to map incoming events to contacts in our contact list. + */ + private OperationSetPersistentPresenceYahooImpl opSetPersPresence = null; + + /** + * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt> + * that created us and that we'll use for retrieving the underlying aim + * connection. + */ + OperationSetTypingNotificationsYahooImpl( + ProtocolProviderServiceYahooImpl provider) + { + this.yahooProvider = provider; + provider.addRegistrationStateChangeListener(new ProviderRegListener()); + } + + /** + * Adds <tt>l</tt> to the list of listeners registered for receiving + * <tt>TypingNotificationEvent</tt>s + * + * @param l the <tt>TypingNotificationsListener</tt> listener that we'd + * like to add + * method + */ + public void addTypingNotificationsListener(TypingNotificationsListener l) + { + synchronized(typingNotificationsListeners) + { + typingNotificationsListeners.add(l); + } + } + + /** + * Removes <tt>l</tt> from the list of listeners registered for receiving + * <tt>TypingNotificationEvent</tt>s + * + * @param l the <tt>TypingNotificationsListener</tt> listener that we'd + * like to remove + */ + public void removeTypingNotificationsListener(TypingNotificationsListener l) + { + synchronized(typingNotificationsListeners) + { + typingNotificationsListeners.remove(l); + } + } + + /** + * Delivers a <tt>TypingNotificationEvent</tt> to all registered listeners. + * @param sourceContact the contact who has sent the notification. + * @param evtCode the code of the event to deliver. + */ + private void fireTypingNotificationsEvent(Contact sourceContact + ,int evtCode) + { + logger.debug("Dispatching a TypingNotif. event to " + + typingNotificationsListeners.size()+" listeners. Contact " + + sourceContact.getAddress() + " has now a typing status of " + + evtCode); + + TypingNotificationEvent evt = new TypingNotificationEvent( + sourceContact, evtCode); + + Iterator listeners = null; + synchronized (typingNotificationsListeners) + { + listeners = new ArrayList(typingNotificationsListeners).iterator(); + } + + while (listeners.hasNext()) + { + TypingNotificationsListener listener + = (TypingNotificationsListener) listeners.next(); + + listener.typingNotificationReceifed(evt); + } + } + + /** + * Sends a notification to <tt>notifiedContatct</tt> that we have entered + * <tt>typingState</tt>. + * + * @param notifiedContact the <tt>Contact</tt> to notify + * @param typingState the typing state that we have entered. + * + * @throws java.lang.IllegalStateException if the underlying stack is + * not registered and initialized. + * @throws java.lang.IllegalArgumentException if <tt>notifiedContact</tt> is + * not an instance belonging to the underlying implementation. + */ + public void sendTypingNotification(Contact notifiedContact, int typingState) + throws IllegalStateException, IllegalArgumentException + { + assertConnected(); + + if( !(notifiedContact instanceof ContactYahooImpl) ) + throw new IllegalArgumentException( + "The specified contact is not an yahoo contact." + notifiedContact); + + if(typingState == OperationSetTypingNotifications.STATE_TYPING) + { + yahooProvider.getYahooSession(). + keyTyped(notifiedContact.getAddress()); + } + } + + /** + * 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 (yahooProvider == null) + throw new IllegalStateException( + "The yahoo provider must be non-null and signed on the " + +"service before being able to communicate."); + if (!yahooProvider.isRegistered()) + throw new IllegalStateException( + "The yahoo provider must be signed on the service before " + +"being able to communicate."); + } + + private class TypingListener + extends SessionAdapter + { + public void notifyReceived(SessionNotifyEvent evt) + { + if(evt.isTyping()) + { + String typingUserID = evt.getFrom(); + + if(typingUserID != null) + { + Contact sourceContact = + opSetPersPresence.findContactByID(typingUserID); + + if(sourceContact == null) + return; + + // typing on + if(evt.getMode() == 1) + fireTypingNotificationsEvent(sourceContact, STATE_TYPING); + else + fireTypingNotificationsEvent(sourceContact, STATE_STOPPED); + } + } + } + } + + /** + * Our listener that will tell us when we're registered and + * ready to accept us as a listener. + */ + private class ProviderRegListener + 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 provider changed state from: " + + evt.getOldState() + + " to: " + evt.getNewState()); + if (evt.getNewState() == RegistrationState.REGISTERED) + { + opSetPersPresence = (OperationSetPersistentPresenceYahooImpl) + yahooProvider.getSupportedOperationSets() + .get(OperationSetPersistentPresence.class.getName()); + + yahooProvider.getYahooSession().addSessionListener(new TypingListener()); + } + } + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java new file mode 100644 index 0000000..b037a6f --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java @@ -0,0 +1,264 @@ +/* + * 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.yahoo; + +import java.util.*; + +import org.osgi.framework.*; +import net.java.sip.communicator.service.protocol.*; + +/** + * The Yahoo implementation of the ProtocolProviderFactory. + * @author Damian Minkov + */ +public class ProtocolProviderFactoryYahooImpl + extends ProtocolProviderFactory +{ + /** + * The table that we store our accounts in. + */ + private Hashtable registeredAccounts = new Hashtable(); + + /** + * Creates an instance of the ProtocolProviderFactoryYahooImpl. + */ + protected ProtocolProviderFactoryYahooImpl() + { + } + + /** + * Returns a copy of the list containing all accounts currently + * registered in this protocol provider. + * + * @return a copy of the llist containing all accounts currently installed + * in the protocol provider. + */ + public ArrayList getRegisteredAccounts() + { + return new ArrayList(registeredAccounts.keySet()); + } + + /** + * Returns the ServiceReference for the protocol provider corresponding to + * the specified accountID or null if the accountID is unknown. + * @param accountID the accountID of the protocol provider we'd like to get + * @return a ServiceReference object to the protocol provider with the + * specified account id and null if the account id is unknwon to the + * provider factory. + */ + public ServiceReference getProviderForAccount(AccountID accountID) + { + ServiceRegistration registration + = (ServiceRegistration)registeredAccounts.get(accountID); + + return (registration == null ) + ? null + : registration.getReference(); + } + + /** + * Initializes and creates an account corresponding to the specified + * accountProperties and registers the resulting ProtocolProvider in the + * <tt>context</tt> BundleContext parameter. This method has a persistent + * effect. Once created the resulting account will remain installed until + * removed through the uninstall account method. + * + * @param userIDStr the user identifier for the new account + * @param accountProperties a set of protocol (or implementation) + * specific properties defining the new account. + * @return the AccountID of the newly created account + */ + public AccountID installAccount( String userIDStr, + Map accountProperties) + { + BundleContext context + = YahooActivator.getBundleContext(); + if (context == null) + throw new NullPointerException("The specified BundleContext was null"); + + if (userIDStr == null) + throw new NullPointerException("The specified AccountID was null"); + + if (accountProperties == null) + throw new NullPointerException("The specified property map was null"); + + accountProperties.put(USER_ID, userIDStr); + + AccountID accountID = new YahooAccountID(userIDStr, accountProperties); + + //make sure we haven't seen this account id before. + if( registeredAccounts.containsKey(accountID) ) + throw new IllegalStateException( + "An account for id " + userIDStr + " was already installed!"); + + //first store the account and only then load it as the load generates + //an osgi event, the osgi event triggers (trhgough the UI) a call to + //the register() method and it needs to acces the configuration service + //and check for a password. + this.storeAccount( + YahooActivator.getBundleContext() + , accountID); + + accountID = loadAccount(accountProperties); + + return accountID; + } + + /** + * Initializes and creates an account corresponding to the specified + * accountProperties and registers the resulting ProtocolProvider in the + * <tt>context</tt> BundleContext parameter. + * + * @param accountProperties a set of protocol (or implementation) + * specific properties defining the new account. + * @return the AccountID of the newly created account + */ + public AccountID loadAccount( Map accountProperties) + { + BundleContext context + = YahooActivator.getBundleContext(); + if(context == null) + throw new NullPointerException("The specified BundleContext was null"); + + String userIDStr = (String)accountProperties.get(USER_ID); + + AccountID accountID = new YahooAccountID(userIDStr, accountProperties); + + //get a reference to the configuration service and register whatever + //properties we have in it. + + Hashtable properties = new Hashtable(); + properties.put(PROTOCOL, ProtocolNames.YAHOO); + properties.put(USER_ID, userIDStr); + + ProtocolProviderServiceYahooImpl yahooProtocolProvider + = new ProtocolProviderServiceYahooImpl(); + + yahooProtocolProvider.initialize(userIDStr, accountID); + + ServiceRegistration registration + = context.registerService( ProtocolProviderService.class.getName(), + yahooProtocolProvider, + properties); + + registeredAccounts.put(accountID, registration); + return accountID; + } + + + /** + * Removes the specified account from the list of accounts that this + * provider factory is handling. If the specified accountID is unknown to + * the ProtocolProviderFactory, the call has no effect and false is returned. + * This method is persistent in nature and once called the account + * corresponding to the specified ID will not be loaded during future runs + * of the project. + * + * @param accountID the ID of the account to remove. + * @return true if an account with the specified ID existed and was removed + * and false otherwise. + */ + public boolean uninstallAccount(AccountID accountID) + { + ServiceRegistration registration + = (ServiceRegistration)registeredAccounts.remove(accountID); + + if(registration == null) + return false; + + //kill the service + registration.unregister(); + + return removeStoredAccount( + YahooActivator.getBundleContext() + , accountID); + } + + /** + * Loads (and hence installs) all accounts previously stored in the + * configuration service. + */ + public void loadStoredAccounts() + { + super.loadStoredAccounts( YahooActivator.getBundleContext()); + } + + /** + * Prepares the factory for bundle shutdown. + */ + public void stop() + { + Enumeration registrations = this.registeredAccounts.elements(); + + while(registrations.hasMoreElements()) + { + ServiceRegistration reg + = ((ServiceRegistration)registrations.nextElement()); + + reg.unregister(); + } + + Enumeration idEnum = registeredAccounts.keys(); + + while(idEnum.hasMoreElements()) + { + registeredAccounts.remove(idEnum.nextElement()); + } + } + + /** + * Returns the configuraiton service property name prefix that we use to + * store properties concerning the account with the specified id. + * @param accountID the AccountID whose property name prefix we're looking + * for. + * @return the prefix of the configuration service property name that + * we're using when storing properties for the specified account. + */ + public String findAccountPrefix(AccountID accountID) + { + return super.findAccountPrefix(YahooActivator.getBundleContext() + , accountID); + } + + /** + * Saves the password for the specified account after scrambling it a bit + * so that it is not visible from first sight (Method remains highly + * insecure). + * + * @param accountID the AccountID for the account whose password we're + * storing. + * @param passwd the password itself. + * + * @throws java.lang.IllegalArgumentException if no account corresponding + * to <tt>accountID</tt> has been previously stored. + */ + public void storePassword(AccountID accountID, String passwd) + throws IllegalArgumentException + { + super.storePassword(YahooActivator.getBundleContext() + , accountID + , passwd); + } + + /** + * Returns the password last saved for the specified account. + * + * @param accountID the AccountID for the account whose password we're + * looking for.. + * + * @return a String containing the password for the specified accountID. + * + * @throws java.lang.IllegalArgumentException if no account corresponding + * to <tt>accountID</tt> has been previously stored. + */ + public String loadPassword(AccountID accountID) + throws IllegalArgumentException + { + return super.loadPassword(YahooActivator.getBundleContext() + , accountID ); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java new file mode 100644 index 0000000..eb08dc6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java @@ -0,0 +1,494 @@ +/* + * 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.yahoo; + +import java.io.*; +import java.util.*; +import java.nio.channels.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; +import ymsg.network.*; +import ymsg.network.event.*; + +/** + * An implementation of the protocol provider service over the Yahoo protocol + * + * @author Damian Minkov + */ +public class ProtocolProviderServiceYahooImpl + implements ProtocolProviderService +{ + private static final Logger logger = + Logger.getLogger(ProtocolProviderServiceYahooImpl.class); + + /** + * The hashtable with the operation sets that we support locally. + */ + private Hashtable supportedOperationSets = new Hashtable(); + + private YahooSession yahooSession = null; + + /** + * indicates whether or not the provider is initialized and ready for use. + */ + private boolean isInitialized = false; + + /** + * We use this to lock access to initialization. + */ + private Object initializationLock = new Object(); + + /** + * A list of all listeners registered for + * <tt>RegistrationStateChangeEvent</tt>s. + */ + private List registrationListeners = new ArrayList(); + + /** + * The identifier of the account that this provider represents. + */ + private AccountID accountID = null; + + /** + * Used when we need to re-register + */ + private SecurityAuthority authority = null; + + private OperationSetPersistentPresenceYahooImpl persistentPresence = null; + + private OperationSetTypingNotificationsYahooImpl typingNotifications = null; + + /** + * Returns the state of the registration of this protocol provider + * @return the <tt>RegistrationState</tt> that this provider is + * currently in or null in case it is in a unknown state. + */ + public RegistrationState getRegistrationState() + { + if(yahooSession != null && + yahooSession.getSessionStatus() == StatusConstants.MESSAGING) + return RegistrationState.REGISTERED; + else + return RegistrationState.UNREGISTERED; + } + + /** + * Starts the registration process. Connection details such as + * registration server, user name/number are provided through the + * configuration service through implementation specific properties. + * + * @param authority the security authority that will be used for resolving + * any security challenges that may be returned during the + * registration or at any moment while wer're registered. + * @throws OperationFailedException with the corresponding code it the + * registration fails for some reason (e.g. a networking error or an + * implementation problem). + */ + public void register(final SecurityAuthority authority) + throws OperationFailedException + { + if(authority == null) + throw new IllegalArgumentException( + "The register method needs a valid non-null authority impl " + + " in order to be able and retrieve passwords."); + + this.authority = authority; + + connectAndLogin(authority); + } + + /** + * Connects and logins to the server + * @param authority SecurityAuthority + * @throws XMPPException if we cannot connect to the server - network problem + * @throws OperationFailedException if login parameters + * as server port are not correct + */ + private void connectAndLogin(SecurityAuthority authority) + throws OperationFailedException + { + synchronized(initializationLock) + { + //verify whether a password has already been stored for this account + String password = YahooActivator. + getProtocolProviderFactory().loadPassword(getAccountID()); + + //decode + if (password == null) + { + //create a default credentials object + UserCredentials credentials = new UserCredentials(); + credentials.setUserName(getAccountID().getUserID()); + + //request a password from the user + credentials = authority.obtainCredentials(ProtocolNames.YAHOO + , credentials); + + //extract the password the user passed us. + char[] pass = credentials.getPassword(); + + // the user didn't provide us a password (canceled the operation) + if(pass == null) + { + fireRegistrationStateChanged( + getRegistrationState(), + RegistrationState.UNREGISTERED, + RegistrationStateChangeEvent.REASON_USER_REQUEST, ""); + return; + } + password = new String(pass); + + + if (credentials.isPasswordPersistent()) + { + YahooActivator.getProtocolProviderFactory() + .storePassword(getAccountID(), password); + } + } + + yahooSession = new YahooSession(); + yahooSession.addSessionListener(new YahooConnectionListener()); + + try + { + yahooSession.login(getAccountID().getUserID(), password); + + if(yahooSession.getSessionStatus()==StatusConstants.MESSAGING) + { + persistentPresence.fireProviderPresenceStatusChangeEvent( + persistentPresence.getPresenceStatus(), + persistentPresence.yahooStatusToPresenceStatus( + yahooSession.getStatus())); + + fireRegistrationStateChanged( + getRegistrationState(), + RegistrationState.REGISTERED, + RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); + } + else + { + fireRegistrationStateChanged( + getRegistrationState(), + RegistrationState.UNREGISTERED, + RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); + } + } + catch (LoginRefusedException ex) + { + fireRegistrationStateChanged( + getRegistrationState(), + RegistrationState.AUTHENTICATION_FAILED, + RegistrationStateChangeEvent.REASON_AUTHENTICATION_FAILED, null); + } + catch (IOException ex) + { + fireRegistrationStateChanged( + getRegistrationState(), + RegistrationState.CONNECTION_FAILED, + RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); + } + } + } + + /** + * Ends the registration of this protocol provider with the service. + */ + public void unregister() + { + unregister(true); + } + + /** + * Unregister and fire the event if requested + * @param fireEvent boolean + */ + void unregister(boolean fireEvent) + { + RegistrationState currRegState = getRegistrationState(); + + try + { + yahooSession.logout(); + } + catch(Exception ex) + { + logger.error("Cannot logout!"); + } + + yahooSession.reset(); + + if(fireEvent) + { + fireRegistrationStateChanged( + currRegState, + RegistrationState.UNREGISTERED, + RegistrationStateChangeEvent.REASON_USER_REQUEST, null); + } + } + + /** + * Indicates whether or not this provider is signed on the service + * @return true if the provider is currently signed on (and hence online) + * and false otherwise. + */ + public boolean isRegistered() + { + return getRegistrationState().equals(RegistrationState.REGISTERED); + } + + /** + * Returns the short name of the protocol that the implementation of this + * provider is based upon (like SIP, Msn, ICQ/AIM, or others for + * example). + * + * @return a String containing the short name of the protocol this + * service is taking care of. + */ + public String getProtocolName() + { + return ProtocolNames.YAHOO; + } + + /** + * Returns an array containing all operation sets supported by the + * current implementation. + * + * @return an array of OperationSet-s supported by this protocol + * provider implementation. + */ + public Map getSupportedOperationSets() + { + return supportedOperationSets; + } + + /** + * Returns the operation set corresponding to the specified class or null + * if this operation set is not supported by the provider implementation. + * + * @param opsetClass the <tt>Class</tt> of the operation set that we're + * looking for. + * @return returns an OperationSet of the specified <tt>Class</tt> if the + * undelying implementation supports it or null otherwise. + */ + public OperationSet getOperationSet(Class opsetClass) + { + return (OperationSet)getSupportedOperationSets() + .get(opsetClass.getName()); + } + + /** + * Initialized the service implementation, and puts it in a sate where it + * could interoperate with other services. It is strongly recomended that + * properties in this Map be mapped to property names as specified by + * <tt>AccountProperties</tt>. + * + * @param screenname the account id/uin/screenname of the account that + * we're about to create + * @param accountID the identifier of the account that this protocol + * provider represents. + * + * @see net.java.sip.communicator.service.protocol.AccountID + */ + protected void initialize(String screenname, + AccountID accountID) + { + synchronized(initializationLock) + { + this.accountID = accountID; + + //initialize the presence operationset + persistentPresence = new OperationSetPersistentPresenceYahooImpl(this); + + supportedOperationSets.put( + OperationSetPersistentPresence.class.getName(), + persistentPresence); + + //register it once again for those that simply need presence + supportedOperationSets.put( OperationSetPresence.class.getName(), + persistentPresence); + + //initialize the IM operation set + OperationSetBasicInstantMessagingYahooImpl basicInstantMessaging = + new OperationSetBasicInstantMessagingYahooImpl(this); + + supportedOperationSets.put( + OperationSetBasicInstantMessaging.class.getName(), + basicInstantMessaging); + + //initialize the typing notifications operation set + typingNotifications = + new OperationSetTypingNotificationsYahooImpl(this); + + supportedOperationSets.put( + OperationSetTypingNotifications.class.getName(), + typingNotifications); + + isInitialized = true; + } + } + + /** + * Makes the service implementation close all open sockets and release + * any resources that it might have taken and prepare for + * shutdown/garbage collection. + */ + public void shutdown() + { + synchronized(initializationLock){ + unregister(false); + yahooSession = null; + isInitialized = false; + } + } + + /** + * Returns true if the provider service implementation is initialized and + * ready for use by other services, and false otherwise. + * + * @return true if the provider is initialized and ready for use and false + * otherwise + */ + public boolean isInitialized() + { + return isInitialized; + } + + /** + * Removes the specified registration state change listener so that it does + * not receive any further notifications upon changes of the + * RegistrationState of this provider. + * + * @param listener the listener to register for + * <tt>RegistrationStateChangeEvent</tt>s. + */ + public void removeRegistrationStateChangeListener( + RegistrationStateChangeListener listener) + { + synchronized(registrationListeners) + { + registrationListeners.remove(listener); + } + } + + /** + * Registers the specified listener with this provider so that it would + * receive notifications on changes of its state or other properties such + * as its local address and display name. + * + * @param listener the listener to register. + */ + public void addRegistrationStateChangeListener( + RegistrationStateChangeListener listener) + { + synchronized(registrationListeners) + { + if (!registrationListeners.contains(listener)) + registrationListeners.add(listener); + } + } + + /** + * Returns the AccountID that uniquely identifies the account represented + * by this instance of the ProtocolProviderService. + * @return the id of the account represented by this provider. + */ + public AccountID getAccountID() + { + return accountID; + } + + /** + * Returns the Yahoo<tt>Session</tt>opened by this provider + * @return a reference to the <tt>Session</tt> last opened by this + * provider. + */ + YahooSession getYahooSession() + { + return yahooSession; + } + + /** + * Creates a RegistrationStateChange event corresponding to the specified + * old and new states and notifies all currently registered listeners. + * + * @param oldState the state that the provider had before the change + * occurred + * @param newState the state that the provider is currently in. + * @param reasonCode a value corresponding to one of the REASON_XXX fields + * of the RegistrationStateChangeEvent class, indicating the reason for + * this state transition. + * @param reason a String further explaining the reason code or null if + * no such explanation is necessary. + */ + void fireRegistrationStateChanged( RegistrationState oldState, + RegistrationState newState, + int reasonCode, + String reason) + { + RegistrationStateChangeEvent event = + new RegistrationStateChangeEvent( + this, oldState, newState, reasonCode, reason); + + logger.debug("Dispatching " + event + " to " + + registrationListeners.size()+ " listeners."); + + if(newState.equals(RegistrationState.UNREGISTERED) || + newState.equals(RegistrationState.CONNECTION_FAILED)) + { + unregister(false); + yahooSession = null; + } + + Iterator listeners = null; + synchronized (registrationListeners) + { + listeners = new ArrayList(registrationListeners).iterator(); + } + + while (listeners.hasNext()) + { + RegistrationStateChangeListener listener + = (RegistrationStateChangeListener) listeners.next(); + + listener.registrationStateChanged(event); + } + + logger.trace("Done."); + } + + /** + * Listens when we are logged in the server + * or incoming exception in the lib impl. + */ + private class YahooConnectionListener + extends SessionAdapter + { + /** + * Yahoo has logged us off the system, or the connection was lost + **/ + public void connectionClosed(SessionEvent ev) + { + unregister(false); + if(isRegistered()) + fireRegistrationStateChanged( + getRegistrationState(), + RegistrationState.CONNECTION_FAILED, + RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); + } + + public void inputExceptionThrown(SessionExceptionEvent ev) + { + unregister(false); + if(isRegistered()) + fireRegistrationStateChanged( + getRegistrationState(), + RegistrationState.UNREGISTERED, + RegistrationStateChangeEvent.REASON_INTERNAL_ERROR, null); + } + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java new file mode 100644 index 0000000..2ce9df8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java @@ -0,0 +1,301 @@ +/* + * 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.yahoo; + +import java.util.*; + +import net.java.sip.communicator.service.protocol.*; + +/** + * A dummy ContactGroup implementation representing the ContactList root for + * Yahoo contact lists. + * @author Damian Minkov + */ +public class RootContactGroupYahooImpl + extends AbstractContactGroupYahooImpl +{ + private String ROOT_CONTACT_GROUP_NAME = "ContactListRoot"; + private List subGroups = new LinkedList(); + private boolean isResolved = false; + + /** + * An empty list that we use when returning an iterator. + */ + private List dummyContacts = new LinkedList(); + + private ProtocolProviderServiceYahooImpl ownerProvider = null; + + /** + * Creates a ContactGroup instance. + */ + RootContactGroupYahooImpl(){} + + /** + * Sets the currently valid provider + * @param ownerProvider ProtocolProviderServiceImpl + */ + void setOwnerProvider(ProtocolProviderServiceYahooImpl ownerProvider) + { + this.ownerProvider = ownerProvider; + } + + /** + * The ContactListRoot is the only group that can contain subgroups. + * + * @return true (always) + */ + public boolean canContainSubgroups() + { + return true; + } + + /** + * Returns the name of this group which is always + * <tt>ROOT_CONTACT_GROUP_NAME</tt>. + * + * @return a String containing the name of this group. + */ + public String getGroupName() + { + return ROOT_CONTACT_GROUP_NAME; + } + + /** + * Adds the specified group to the end of the list of sub groups. + * @param group the group to add. + */ + void addSubGroup(ContactGroupYahooImpl group) + { + subGroups.add(group); + } + + /** + * Removes the specified from the list of sub groups + * @param group the group to remove. + */ + void removeSubGroup(ContactGroupYahooImpl group) + { + removeSubGroup(subGroups.indexOf(group)); + } + + /** + * Removes the sub group with the specified index. + * @param index the index of the group to remove + */ + void removeSubGroup(int index) + { + subGroups.remove(index); + } + + /** + * Removes all contact sub groups and reinsterts them as specified + * by the <tt>newOrder</tt> param. Contact groups not contained in the + * newOrder list are left at the end of this group. + * + * @param newOrder a list containing all contact groups in the order that is + * to be applied. + * + */ + void reorderSubGroups(List newOrder) + { + subGroups.removeAll(newOrder); + subGroups.addAll(0, newOrder); + } + + /** + * Returns the number of subgroups contained by this + * <tt>RootContactGroupImpl</tt>. + * + * @return an int indicating the number of subgroups that this + * ContactGroup contains. + */ + public int countSubgroups() + { + return subGroups.size(); + } + + /** + * Returns null as this is the root contact group. + * @return null as this is the root contact group. + */ + public ContactGroup getParentContactGroup() + { + return null; + } + + /** + * Returns the subgroup with the specified index. + * + * @param index the index of the <tt>ContactGroup</tt> to retrieve. + * @return the <tt>ContactGroup</tt> with the specified index. + */ + public ContactGroup getGroup(int index) + { + return (ContactGroupYahooImpl) subGroups.get(index); + } + + /** + * Returns the subgroup with the specified name. + * @param groupName the name of the <tt>ContactGroup</tt> to retrieve. + * @return the <tt>ContactGroup</tt> with the specified index. + */ + public ContactGroup getGroup(String groupName) + { + Iterator subgroups = subgroups(); + while (subgroups.hasNext()) + { + ContactGroupYahooImpl grp = (ContactGroupYahooImpl) subgroups.next(); + + if (grp.getGroupName().equals(groupName)) + return grp; + } + + return null; + } + + /** + * Returns an iterator over the sub groups that this + * <tt>ContactGroup</tt> contains. + * + * @return a java.util.Iterator over the <tt>ContactGroup</tt> + * children of this group (i.e. subgroups). + */ + public Iterator subgroups() + { + return subGroups.iterator(); + } + + /** + * Returns the number, which is always 0, of <tt>Contact</tt> members + * of this <tt>ContactGroup</tt> + * @return an int indicating the number of <tt>Contact</tt>s, members + * of this <tt>ContactGroup</tt>. + */ + public int countContacts() + { + return dummyContacts.size(); + } + + /** + * Returns an Iterator over all contacts, member of this + * <tt>ContactGroup</tt>. + * @return a java.util.Iterator over all contacts inside this + * <tt>ContactGroup</tt> + */ + public Iterator contacts() + { + return dummyContacts.iterator(); + } + + /** + * A dummy impl of the corresponding interface method - always returns null. + * + * @param index the index of the <tt>Contact</tt> to return. + * @return the <tt>Contact</tt> with the specified index, i.e. always + * null. + */ + public Contact getContact(int index) + { + return null; + } + + /** + * Returns the <tt>Contact</tt> with the specified address or + * identifier. + * @param id the addres or identifier of the <tt>Contact</tt> we are + * looking for. + * @return the <tt>Contact</tt> with the specified id or address. + */ + public Contact getContact(String id) + { + //no contacts in the root group for this yahoo impl. + return null; + } + + + /** + * Returns a string representation of the root contact group that contains + * all subgroups and subcontacts of this group. + * + * @return a string representation of this root contact group. + */ + public String toString() + { + StringBuffer buff = new StringBuffer(getGroupName()); + buff.append(".subGroups=" + countSubgroups() + ":\n"); + + Iterator subGroups = subgroups(); + while (subGroups.hasNext()) + { + ContactGroup group = (ContactGroup) subGroups.next(); + buff.append(group.toString()); + if (subGroups.hasNext()) + buff.append("\n"); + } + return buff.toString(); + } + + /** + * Returns the protocol provider that this group belongs to. + * @return a regerence to the ProtocolProviderService instance that this + * ContactGroup belongs to. + */ + public ProtocolProviderService getProtocolProvider() + { + return this.ownerProvider; + } + + /** + * Determines whether or not this contact group is being stored by the + * server. Non persistent contact groups exist for the sole purpose of + * containing non persistent contacts. + * @return true if the contact group is persistent and false otherwise. + */ + public boolean isPersistent() + { + return true; + } + + /** + * Returns null as no persistent data is required and the group name is + * sufficient for restoring the contact. + * <p> + * @return null as no such data is needed. + */ + public String getPersistentData() + { + return null; + } + + /** + * Determines whether or not this group has been resolved against the + * server. Unresolved groups are used when initially loading a contact + * list that has been stored in a local file until the presence operation + * set has managed to retrieve all the contact list from the server and has + * properly mapped groups to their on-line buddies. + * @return true if the group has been resolved (mapped against a buddy) + * and false otherwise. + */ + public boolean isResolved() + { + return isResolved; + } + + /** + * Returns a <tt>String</tt> that uniquely represnets the group. In this we + * use the name of the group as an identifier. This may cause problems + * though, in clase the name is changed by some other application between + * consecutive runs of the sip-communicator. + * + * @return a String representing this group in a unique and persistent + * way. + */ + public String getUID() + { + return getGroupName(); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java new file mode 100644 index 0000000..699fdce --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java @@ -0,0 +1,1027 @@ +/* + * 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.yahoo; + +import java.io.*; +import java.util.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; +import ymsg.network.*; +import ymsg.network.event.*; + +/** + * This class encapsulates the Roster class. Once created, it will + * register itself as a listener to the encapsulated Roster and modify it's + * local copy of Contacts and ContactGroups every time an event is generated + * by the underlying framework. The class would also generate + * corresponding sip-communicator events to all events coming from smack. + * + * @author Damian Minkov + */ +public class ServerStoredContactListYahooImpl +{ + private static final Logger logger = + Logger.getLogger(ServerStoredContactListYahooImpl.class); + + /** + * The name of the Volatile group + */ + private static final String VOLATILE_GROUP_NAME = "NotInContactList"; + + /** + * If there is no group and we add contact with no parent + * a default group is created with name : DEFAULT_GROUP_NAME + */ + private static final String DEFAULT_GROUP_NAME = "General"; + + /** + * The root contagroup. The container for all yahoo buddies and groups. + */ + private RootContactGroupYahooImpl rootGroup = new RootContactGroupYahooImpl(); + + /** + * The operation set that created us and that we could use when dispatching + * subscription events. + */ + private OperationSetPersistentPresenceYahooImpl parentOperationSet = null; + + /** + * The provider that is on top of us. + */ + private ProtocolProviderServiceYahooImpl yahooProvider = null; + + private YahooSession yahooSession = null; + + /** + * Listeners that would receive event notifications for changes in group + * names or other properties, removal or creation of groups. + */ + private Vector serverStoredGroupListeners = new Vector(); + + private ContactListModListenerImpl contactListModListenerImpl + = new ContactListModListenerImpl(); + + /** + * indicates whether or not the contactlist is initialized and ready. + */ + private boolean isInitialized = false; + + /** + * Handler for incoming authorization requests. + */ + private AuthorizationHandler handler = null; + + /** + * Creates a ServerStoredContactList wrapper for the specified BuddyList. + * + * @param parentOperationSet the operation set that created us and that + * we could use for dispatching subscription events + * @param provider the provider that has instantiated us. + */ + ServerStoredContactListYahooImpl( + OperationSetPersistentPresenceYahooImpl parentOperationSet, + ProtocolProviderServiceYahooImpl provider) + { + //We need to init these as early as possible to ensure that the provider + //and the operationsset would not be null in the incoming events. + this.parentOperationSet = parentOperationSet; + + this.yahooProvider = provider; + rootGroup.setOwnerProvider(provider); + + // listens for provider registered events to set the isInitialized state + // of the contact list + provider.addRegistrationStateChangeListener( + new RegistrationStateChangeListener() + { + public void registrationStateChanged( + RegistrationStateChangeEvent evt) + { + if (evt.getNewState() == RegistrationState.UNREGISTERED + || evt.getNewState() == RegistrationState.AUTHENTICATION_FAILED + || evt.getNewState() == RegistrationState.CONNECTION_FAILED) + { + isInitialized = false; + } + } + } + ); + } + + /** + * Handler for incoming authorization requests. + * + * @param handler an instance of an AuthorizationHandler for + * authorization requests coming from other users requesting + * permission add us to their contact list. + */ + public void setAuthorizationHandler(AuthorizationHandler handler) + { + this.handler = handler; + } + + /** + * Returns the root group of the contact list. + * + * @return the root ContactGroup for the ContactList + */ + public ContactGroup getRootGroup() + { + return rootGroup; + } + + /** + * Registers the specified group listener so that it would receive events + * on group modification/creation/destruction. + * @param listener the ServerStoredGroupListener to register for group events + */ + void addGroupListener(ServerStoredGroupListener listener) + { + synchronized(serverStoredGroupListeners) + { + if(!serverStoredGroupListeners.contains(listener)) + this.serverStoredGroupListeners.add(listener); + } + } + + /** + * Removes the specified group listener so that it won't receive further + * events on group modification/creation/destruction. + * @param listener the ServerStoredGroupListener to unregister + */ + void removeGroupListener(ServerStoredGroupListener listener) + { + synchronized(serverStoredGroupListeners) + { + this.serverStoredGroupListeners.remove(listener); + } + } + + /** + * Creates the corresponding event and notifies all + * <tt>ServerStoredGroupListener</tt>s that the source group has been + * removed, changed, renamed or whatever happened to it. + * @param group the ContactGroup that has been created/modified/removed + * @param eventID the id of the event to generate. + */ + private void fireGroupEvent(ContactGroupYahooImpl group, int eventID) + { + //bail out if no one's listening + if(parentOperationSet == null){ + logger.debug("No presence op. set available. Bailing out."); + return; + } + + ServerStoredGroupEvent evt = new ServerStoredGroupEvent( + group + , eventID + , parentOperationSet.getServerStoredContactListRoot() + , yahooProvider + , parentOperationSet); + + logger.trace("Will dispatch the following grp event: " + evt); + + Iterator listeners = null; + synchronized (serverStoredGroupListeners) + { + listeners = new ArrayList(serverStoredGroupListeners).iterator(); + } + + while (listeners.hasNext()) + { + ServerStoredGroupListener listener + = (ServerStoredGroupListener) listeners.next(); + + if (eventID == ServerStoredGroupEvent.GROUP_REMOVED_EVENT) + listener.groupRemoved(evt); + else if (eventID == ServerStoredGroupEvent.GROUP_RENAMED_EVENT) + listener.groupNameChanged(evt); + else if (eventID == ServerStoredGroupEvent.GROUP_CREATED_EVENT) + listener.groupCreated(evt); + else if (eventID == ServerStoredGroupEvent.GROUP_RESOLVED_EVENT) + listener.groupResolved(evt); + } + } + + /** + * Make the parent persistent presence operation set dispatch a contact + * removed event. + * @param parentGroup the group where that the removed contact belonged to. + * @param contact the contact that was removed. + */ + private void fireContactRemoved( ContactGroup parentGroup, + ContactYahooImpl contact) + { + //bail out if no one's listening + if(parentOperationSet == null){ + logger.debug("No presence op. set available. Bailing out."); + return; + } + + //dispatch + parentOperationSet.fireSubscriptionEvent( + SubscriptionEvent.SUBSCRIPTION_REMOVED, contact, parentGroup); + } + + /** + * Make the parent persistent presence operation set dispatch a subscription + * moved event. + * @param oldParentGroup the group where the source contact was located + * before being moved + * @param newParentGroup the group that the source contact is currently in. + * @param contact the contact that was added + */ + private void fireContactMoved( ContactGroup oldParentGroup, + ContactGroupYahooImpl newParentGroup, + ContactYahooImpl contact) + { + //bail out if no one's listening + if(parentOperationSet == null){ + logger.debug("No presence op. set available. Bailing out."); + return; + } + + //dispatch + parentOperationSet.fireSubscriptionMovedEvent( + contact, oldParentGroup, newParentGroup); + } + + /** + * Retrns a reference to the provider that created us. + * @return a reference to a ProtocolProviderServiceImpl instance. + */ + ProtocolProviderServiceYahooImpl getParentProvider() + { + return yahooProvider; + } + + /** + * Returns the ConntactGroup with the specified name or null if no such + * group was found. + * <p> + * @param name the name of the group we're looking for. + * @return a reference to the ContactGroupYahooImpl instance we're looking for + * or null if no such group was found. + */ + public ContactGroupYahooImpl findContactGroup(String name) + { + Iterator contactGroups = rootGroup.subgroups(); + + while(contactGroups.hasNext()) + { + ContactGroupYahooImpl contactGroup + = (ContactGroupYahooImpl) contactGroups.next(); + + if (contactGroup.getGroupName().equals(name)) + return contactGroup; + } + + return null; + } + + /** + * Returns the Contact with the specified id or null if + * no such id was found. + * + * @param id the id of the contact to find. + * @return the <tt>Contact</tt> carrying the specified + * <tt>screenName</tt> or <tt>null</tt> if no such contact exits. + */ + public ContactYahooImpl findContactById(String id) + { + Iterator contactGroups = rootGroup.subgroups(); + ContactYahooImpl result = null; + + while(contactGroups.hasNext()) + { + ContactGroupYahooImpl contactGroup + = (ContactGroupYahooImpl) contactGroups.next(); + + result = contactGroup.findContact(id); + + if (result != null) + return result; + + } + + Iterator rootContacts = rootGroup.contacts(); + while (rootContacts.hasNext()) + { + ContactYahooImpl item = (ContactYahooImpl) rootContacts.next(); + + if(item.getAddress().equals(id)) + return item; + } + + return null; + } + + /** + * Returns the ContactGroup containing the specified contact or null + * if no such group or contact exist. + * + * @param child the contact whose parent group we're looking for. + * @return the <tt>ContactGroup</tt> containing the specified + * <tt>contact</tt> or <tt>null</tt> if no such groupo or contact + * exist. + */ + public ContactGroup findContactGroup(ContactYahooImpl child) + { + Iterator contactGroups = rootGroup.subgroups(); + + while(contactGroups.hasNext()) + { + ContactGroupYahooImpl contactGroup + = (ContactGroupYahooImpl) contactGroups.next(); + + if( contactGroup.findContact(child.getAddress())!= null) + return contactGroup; + } + + Iterator contacts = rootGroup.contacts(); + + while(contacts.hasNext()) + { + ContactYahooImpl contact = (ContactYahooImpl) contacts.next(); + + if( contact.equals(child)) + return rootGroup; + } + + return null; + } + + /** + * Adds a new contact with the specified screenname to the list under a + * default location. + * @param id the id of the contact to add. + * @throws OperationFailedException + */ + public void addContact(String id) + throws OperationFailedException + { + ContactGroupYahooImpl parent = getFirstPersistentGroup(); + + if(parent == null) + { + // if there is no group create it + parent = createUnresolvedContactGroup(DEFAULT_GROUP_NAME); + } + + addContact(parent, id); + } + + /** + * Adds a new contact with the specified screenname to the list under the + * specified group. + * @param id the id of the contact to add. + * @param parent the group under which we want the new contact placed. + * @throws OperationFailedException if the contact already exist + */ + public void addContact(final ContactGroupYahooImpl parent, final String id) + throws OperationFailedException + { + logger.trace("Adding contact " + id + " to parent=" + parent); + + //if the contact is already in the contact list and is not volatile, + //then only broadcast an event + ContactYahooImpl existingContact = findContactById(id); + + if( existingContact != null + && existingContact.isPersistent() ) + { + logger.debug("Contact " + id + " already exists."); + throw new OperationFailedException( + "Contact " + id + " already exists.", + OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS); + } + + try + { + yahooSession.addFriend(id, parent.getGroupName()); + } + catch(IOException ex) + { + throw new OperationFailedException( + "Contact cannot be added " + id, + OperationFailedException.NETWORK_FAILURE); + } + } + + /** + * Creates a non persistent contact for the specified address. This would + * also create (if necessary) a group for volatile contacts that would not + * be added to the server stored contact list. This method would have no + * effect on the server stored contact list. + * @param id the address of the contact to create. + * @return the newly created volatile <tt>ContactImpl</tt> + */ + ContactYahooImpl createVolatileContact(String id) + { + VolatileContactYahooImpl newVolatileContact + = new VolatileContactYahooImpl(id, this); + + //Check whether a volatile group already exists and if not create + //one + ContactGroupYahooImpl theVolatileGroup = getNonPersistentGroup(); + + //if the parent group is null then add necessary create the group + if (theVolatileGroup == null) + { + theVolatileGroup = new VolatileContactGroupYahooImpl( + VOLATILE_GROUP_NAME, this); + + theVolatileGroup.addContact(newVolatileContact); + + this.rootGroup.addSubGroup(theVolatileGroup); + + fireGroupEvent(theVolatileGroup + , ServerStoredGroupEvent.GROUP_CREATED_EVENT); + } + else + { + theVolatileGroup.addContact(newVolatileContact); + + fireContactAdded(theVolatileGroup, newVolatileContact); + } + + return newVolatileContact; + } + + + /** + * Creates a non resolved contact for the specified address and inside the + * specified group. The newly created contact would be added to the local + * contact list as a standard contact but when an event is received from the + * server concerning this contact, then it will be reused and only its + * isResolved field would be updated instead of creating the whole contact + * again. + * + * @param parentGroup the group where the unersolved contact is to be + * created + * @param id the Address of the contact to create. + * @return the newly created unresolved <tt>ContactImpl</tt> + */ + ContactYahooImpl createUnresolvedContact(ContactGroup parentGroup, String id) + { + ContactYahooImpl newUnresolvedContact + = new ContactYahooImpl(id, this, false); + + if(parentGroup instanceof ContactGroupYahooImpl) + ((ContactGroupYahooImpl)parentGroup). + addContact(newUnresolvedContact); + + fireContactAdded(parentGroup, newUnresolvedContact); + + return newUnresolvedContact; + } + + /** + * Creates a non resolved contact group for the specified name. The newly + * created group would be added to the local contact list as any other group + * but when an event is received from the server concerning this group, then + * it will be reused and only its isResolved field would be updated instead + * of creating the whole group again. + * <p> + * @param groupName the name of the group to create. + * @return the newly created unresolved <tt>ContactGroupImpl</tt> + */ + ContactGroupYahooImpl createUnresolvedContactGroup(String groupName) + { + ContactGroupYahooImpl newUnresolvedGroup = + new ContactGroupYahooImpl(groupName, this); + + this.rootGroup.addSubGroup(newUnresolvedGroup); + + fireGroupEvent(newUnresolvedGroup + , ServerStoredGroupEvent.GROUP_CREATED_EVENT); + + return newUnresolvedGroup; + } + + /** + * Creates the specified group on the server stored contact list. + * @param groupName a String containing the name of the new group. + * @throws OperationFailedException with code CONTACT_GROUP_ALREADY_EXISTS + * if the group we're trying to create is already in our contact list. + */ + public void createGroup(String groupName) + throws OperationFailedException + { + logger.trace("Creating group: " + groupName); + + ContactGroupYahooImpl existingGroup = findContactGroup(groupName); + + if( existingGroup != null && existingGroup.isPersistent() ) + { + logger.debug("ContactGroup " + groupName + " already exists."); + throw new OperationFailedException( + "ContactGroup " + groupName + " already exists.", + OperationFailedException.CONTACT_GROUP_ALREADY_EXISTS); + } + + // create unresolved group if friend is added - group will be resolved + createUnresolvedContactGroup(groupName); + } + + /** + * Removes the specified group from the buddy list. + * @param groupToRemove the group that we'd like removed. + */ + public void removeGroup(ContactGroupYahooImpl groupToRemove) + { + // to remove group just remove all the contacts in it + + logger.trace("removing group " + groupToRemove); + + // if its not persistent group just remove it +// if(!groupToRemove.isPersistent()) +// { +// rootGroup.removeSubGroup(groupToRemove); +// fireGroupEvent(groupToRemove, +// ServerStoredGroupEvent.GROUP_REMOVED_EVENT); +// return; +// } + + Vector contacts = groupToRemove.getSourceGroup().getMembers(); + + if(contacts.size() == 0) + { + // the group is empty just remove it + rootGroup.removeSubGroup(groupToRemove); + fireGroupEvent(groupToRemove, + ServerStoredGroupEvent.GROUP_REMOVED_EVENT); + return; + } + + Iterator iter = contacts.iterator(); + while(iter.hasNext()) + { + YahooUser item = (YahooUser)iter.next(); + + try + { + yahooSession.removeFriend(item.getId(), groupToRemove.getGroupName()); + } + catch(IOException ex) + { + logger.info("Cannot Remove contact " + item.getId()); + } + } + } + + /** + * Removes a contact from the serverside list + * Event will come for successful operation + * @param contactToRemove ContactYahooImpl + */ + void removeContact(ContactYahooImpl contactToRemove) + { + logger.trace("Removing yahoo contact " + contactToRemove.getSourceContact()); + try + { + yahooSession.removeFriend( + contactToRemove.getSourceContact().getId(), + contactToRemove.getParentContactGroup().getGroupName()); + } + catch(IOException ex) + { + logger.info("Cannot Remove contact " + contactToRemove); + } + } + + /** + * Renames the specified group according to the specified new name.. + * @param groupToRename the group that we'd like removed. + * @param newName the new name of the group + */ + public void renameGroup(ContactGroupYahooImpl groupToRename, String newName) + { + // not working + /* + try + { + yahooSession.renameGroup(groupToRename.getGroupName(), newName); + } + catch(IOException ex) + { + logger.info("Cannot rename group " + groupToRename); + } + + fireGroupEvent(groupToRename, ServerStoredGroupEvent.GROUP_RENAMED_EVENT); + */ + } + + /** + * Moves the specified <tt>contact</tt> to the group indicated by + * <tt>newParent</tt>. + * @param contact the contact that we'd like moved under the new group. + * @param newParent the group where we'd like the parent placed. + */ + public void moveContact(ContactYahooImpl contact, + ContactGroupYahooImpl newParent) + { + try + { + contactListModListenerImpl. + waitForMove(contact.getSourceContact().getId(), + contact.getParentContactGroup().getGroupName()); + + yahooSession.addFriend( + contact.getSourceContact().getId(), + newParent.getGroupName()); + } + catch(IOException ex) + { + contactListModListenerImpl. + removeWaitForMove(contact.getSourceContact().getId()); + logger.error("Contact cannot be added " + ex.getMessage()); + } + } + + /** + * Returns the volatile group + * + * @return ContactGroupYahooImpl + */ + private ContactGroupYahooImpl getNonPersistentGroup() + { + for (int i = 0; i < getRootGroup().countSubgroups(); i++) + { + ContactGroupYahooImpl gr = + (ContactGroupYahooImpl)getRootGroup().getGroup(i); + + if(!gr.isPersistent()) + return gr; + } + + return null; + } + + /** + * Returns the first persistent group + * + * @return ContactGroupIcqImpl + */ + private ContactGroupYahooImpl getFirstPersistentGroup() + { + for (int i = 0; i < getRootGroup().countSubgroups(); i++) + { + ContactGroupYahooImpl gr = + (ContactGroupYahooImpl)getRootGroup().getGroup(i); + + if(gr.isPersistent()) + return gr; + } + + return null; + } + + /** + * Finds Group by provided its yahoo ID + * @param id String + * @return ContactGroupYahooImpl + */ + private ContactGroupYahooImpl findContactGroupByYahooId(String id) + { + Iterator contactGroups = rootGroup.subgroups(); + + while(contactGroups.hasNext()) + { + ContactGroupYahooImpl contactGroup + = (ContactGroupYahooImpl) contactGroups.next(); + + if (contactGroup.getSourceGroup().getName().equals(id)) + return contactGroup; + } + + return null; + } + + /** + * Make the parent persistent presence operation set dispatch a contact + * added event. + * @param parentGroup the group where the new contact was added + * @param contact the contact that was added + */ + void fireContactAdded( ContactGroup parentGroup, + ContactYahooImpl contact) + { + //bail out if no one's listening + if(parentOperationSet == null){ + logger.debug("No presence op. set available. Bailing out."); + return; + } + + //dispatch + parentOperationSet.fireSubscriptionEvent( + SubscriptionEvent.SUBSCRIPTION_CREATED, contact, parentGroup); + } + + /** + * Make the parent persistent presence operation set dispatch a contact + * resolved event. + * @param parentGroup the group that the resolved contact belongs to. + * @param contact the contact that was resolved + */ + void fireContactResolved( ContactGroup parentGroup, + ContactYahooImpl contact) + { + //bail out if no one's listening + if(parentOperationSet == null){ + logger.debug("No presence op. set available. Bailing out."); + return; + } + + //dispatch + parentOperationSet.fireSubscriptionEvent( + SubscriptionEvent.SUBSCRIPTION_RESOLVED, contact, parentGroup); + } + + /** + * Returns true if the contact list is initialized and + * ready for use, and false otherwise. + * + * @return true if the contact list is initialized and ready for use and false + * otherwise + */ + boolean isInitialized() + { + return isInitialized; + } + + /** + * When the protocol is online this method is used to fill or resolve + * the current contact list + */ + private void initList() + { + logger.trace("Start init list of " + yahooProvider.getAccountID().getUserID()); + + YahooGroup[] groups = yahooSession.getGroups(); + + for (int i = 0; i < groups.length; i++) + { + YahooGroup item = groups[i]; + + ContactGroupYahooImpl group = findContactGroup(item.getName()); + + if(group == null) + { + // create the group as it doesn't exist + group = + new ContactGroupYahooImpl(item, item.getMembers(), this, true); + + rootGroup.addSubGroup(group); + + //tell listeners about the added group + fireGroupEvent(group, ServerStoredGroupEvent.GROUP_CREATED_EVENT); + } + else + { + // the group exist so just resolved. The group will check and + // create or resolve its entries + group.setResolved(item); + + //fire an event saying that the group has been resolved + fireGroupEvent(group + , ServerStoredGroupEvent.GROUP_RESOLVED_EVENT); + + /** @todo if something to delete . delete it */ + } + + logger.trace("Init of group done! : " + group); + } + } + + /** + * @param name Name of the group to search + * @return The yahoo group with given name + */ + private YahooGroup findGroup(String name) + { + YahooGroup[] groups = yahooSession.getGroups(); + for (int i = 0; i < groups.length; i++) + { + YahooGroup elem = groups[i]; + if(elem.getName().equals(name)) + return elem; + } + return null; + } + + /** + * Imulates firing adding contact in group and moving contact to group. + * When moving contact it is first adding to the new group then + * it is removed from the old one. + */ + private class ContactListModListenerImpl + extends SessionAdapter + { + private Hashtable waitMove = new Hashtable(); + + public void waitForMove(String id, String oldParent) + { + waitMove.put(id, oldParent); + } + + public void removeWaitForMove(String id) + { + waitMove.remove(id); + } + + /** + * Successfully added a friend + * friend - YahooUser of friend + * group - name of group added to + */ + public void friendAddedReceived(SessionFriendEvent ev) + { + logger.trace("Receive event for adding a friend : " + ev); + + ContactGroupYahooImpl group = + findContactGroup(ev.getGroup()); + + if(group == null){ + logger.trace("Group not found!" + group); + return; + } + + // if group is note resolved resolve it + // this means newly created group + if(!group.isResolved()) + { + YahooGroup gr = findGroup(ev.getGroup()); + + if(gr != null) + group.setResolved(gr); + + // contact will be added when resolving the group + + return; + } + String contactID = ev.getFriend().getId(); + ContactYahooImpl contactToAdd = findContactById(contactID); + + if(contactToAdd == null) + { + contactToAdd = + new ContactYahooImpl(ev.getFriend(), + ServerStoredContactListYahooImpl.this, true, true); + } + else + if(!contactToAdd.isPersistent()) + { + // we must remove the volatile buddy as we will add + // the persistent one + ContactGroupYahooImpl parent = + (ContactGroupYahooImpl)contactToAdd.getParentContactGroup(); + parent.removeContact(contactToAdd); + + contactToAdd = + new ContactYahooImpl(ev.getFriend(), + ServerStoredContactListYahooImpl.this, true, true); + } + + Object isWaitingForMove = waitMove.get(contactID); + + if(isWaitingForMove != null && isWaitingForMove instanceof String) + { + // waits for move into group + // will remove it from old group and will wait for event remove + // from group, then will fire moved to group event + String oldParent = (String)isWaitingForMove; + + group.addContact(contactToAdd); + waitMove.put(contactID, group.getSourceGroup()); + try + { + yahooSession.removeFriend(contactID, oldParent); + } + catch(IOException ex) + { + logger.info("Cannot Remove(till moving) contact :" + + contactToAdd + " from group " + oldParent); + } + } + else + { + group.addContact(contactToAdd); + fireContactAdded(group, contactToAdd); + } + } + + /** + * Successfully removed a friend + * friend - YahooUser of friend + * group - name of group removed from + */ + public void friendRemovedReceived(SessionFriendEvent ev) + { + String contactID = ev.getFriend().getId(); + + // first check is this part of move action + Object waitForMoveObj = waitMove.get(contactID); + if(waitForMoveObj != null && waitForMoveObj instanceof YahooGroup) + { + // first get the group - oldParent + ContactGroupYahooImpl oldParent = findContactGroup(ev.getGroup()); + ContactYahooImpl contactToRemove = oldParent.findContact(contactID); + + oldParent.removeContact(contactToRemove); + waitMove.remove(contactID); + + ContactGroupYahooImpl newParent = + findContactGroup(((YahooGroup)waitForMoveObj).getName()); + + fireContactMoved(oldParent, newParent, contactToRemove); + return; + } + + ContactYahooImpl contactToRemove = findContactById(contactID); + + ContactGroupYahooImpl parentGroup = + (ContactGroupYahooImpl)contactToRemove.getParentContactGroup(); + parentGroup.removeContact(contactToRemove); + fireContactRemoved(parentGroup, contactToRemove); + + // check if the group is deleted. If the contact is the last one in + // the group. The group is also deleted + if(findGroup(ev.getGroup()) == null) + { + rootGroup.removeSubGroup(parentGroup); + fireGroupEvent(parentGroup, ServerStoredGroupEvent.GROUP_REMOVED_EVENT); + } + } + + /** + * Someone wants to add us to their friends list + * to - the target (us!) + * from - the user who wants to add us + * message - the request message text + */ + public void contactRequestReceived(SessionEvent ev) + { + logger.info("contactRequestReceived : " + ev); + + if(handler == null || ev.getFrom() == null) + return; + + ContactYahooImpl contact = findContactById(ev.getFrom()); + + if(contact == null) + contact = parentOperationSet.createVolatileContact(ev.getFrom()); + + AuthorizationRequest request = new AuthorizationRequest(); + request.setReason(ev.getMessage()); + + AuthorizationResponse resp = + handler.processAuthorisationRequest(request, contact); + + if (resp.getResponseCode() == AuthorizationResponse.REJECT) + { + try{ + yahooSession.rejectContact(ev, resp.getReason()); + }catch(IOException ex){ + logger.error("Cannot send reject : " + ex.getMessage()); + } + } + } + + /** + * Someone has rejected our attempts to add them to our friends list + * from - the user who rejected us + * message - rejection message text + */ + public void contactRejectionReceived(SessionEvent ev) + { + logger.info("contactRejectionReceived : " + ev); + + if(handler == null) + return; + + ContactYahooImpl contact = findContactById(ev.getFrom()); + + AuthorizationResponse resp = + new AuthorizationResponse(AuthorizationResponse.REJECT, ev.getMessage()); + handler.processAuthorizationResponse(resp, contact); + } + } + + /** + * Sets the yahoo session instance of the lib + * which comunicates with the server + * @param session YahooSession + */ + void setYahooSession(YahooSession session) + { + this.yahooSession = session; + session.addSessionListener(contactListModListenerImpl); + initList(); + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java new file mode 100644 index 0000000..a5d0e5a --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java @@ -0,0 +1,79 @@ +/* + * 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.yahoo; + +import java.util.*; + +/** + * The Yahoo implementation of the Volatile ContactGroup interface. + * + * @author Damian Minkov + */ +public class VolatileContactGroupYahooImpl + extends ContactGroupYahooImpl +{ + /** + * This contact group name + */ + private String contactGroupName = null; + + /** + * Creates an Yahoo group using the specified group name + * @param groupName String groupname + * @param ssclCallback a callback to the server stored contact list + * we're creating. + */ + VolatileContactGroupYahooImpl( + String groupName, + ServerStoredContactListYahooImpl ssclCallback) + { + super(groupName, ssclCallback); + this.contactGroupName = groupName; + } + + /** + * Returns the name of this group. + * @return a String containing the name of this group. + */ + public String getGroupName() + { + return contactGroupName; + } + + /** + * Returns a string representation of this group, in the form + * YahooGroup.GroupName[size]{ buddy1.toString(), buddy2.toString(), ...}. + * @return a String representation of the object. + */ + public String toString() + { + StringBuffer buff = new StringBuffer("VolatileYahooGroup."); + buff.append(getGroupName()); + buff.append(", childContacts="+countContacts()+":["); + + Iterator contacts = contacts(); + while (contacts.hasNext()) + { + ContactYahooImpl contact = (ContactYahooImpl) contacts.next(); + buff.append(contact.toString()); + if(contacts.hasNext()) + buff.append(", "); + } + return buff.append("]").toString(); + } + + /** + * Determines whether or not this contact group is being stored by the + * server. Non persistent contact groups exist for the sole purpose of + * containing non persistent contacts. + * @return true if the contact group is persistent and false otherwise. + */ + public boolean isPersistent() + { + return false; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactYahooImpl.java new file mode 100644 index 0000000..285f6e5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactYahooImpl.java @@ -0,0 +1,81 @@ +/* + * 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.yahoo; + +import net.java.sip.communicator.util.Logger; + +/** + * The Yahoo implementation for Volatile Contact + * @author Damian Minkov + */ +public class VolatileContactYahooImpl + extends ContactYahooImpl +{ + /** + * This contact id + */ + private String contactId = null; + /** + * Creates an Volatile YahooContactImpl with the specified id + * @param id String the user id/address + * @param ssclCallback a reference to the ServerStoredContactListImpl + * instance that created us. + */ + VolatileContactYahooImpl(String id, + ServerStoredContactListYahooImpl ssclCallback) + { + super(id, ssclCallback, false); + this.contactId = id; + } + + /** + * Returns the Yahoo Userid of this contact + * @return the Yahoo Userid of this contact + */ + public String getAddress() + { + return contactId; + } + + /** + * Returns a String that could be used by any user interacting modules for + * referring to this contact. An alias is not necessarily unique but is + * often more human readable than an address (or id). + * @return a String that can be used for referring to this contact when + * interacting with the user. + */ + public String getDisplayName() + { + return contactId; + } + + /** + * Returns a string representation of this contact, containing most of its + * representative details. + * + * @return a string representation of this contact. + */ + public String toString() + { + StringBuffer buff = new StringBuffer("VolatileYahooContact[ id="); + buff.append(getAddress()).append("]"); + + return buff.toString(); + } + + /** + * Determines whether or not this contact group is being stored by the + * server. Non persistent contact groups exist for the sole purpose of + * containing non persistent contacts. + * @return true if the contact group is persistent and false otherwise. + */ + public boolean isPersistent() + { + return false; + } + +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java new file mode 100644 index 0000000..5662fd5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java @@ -0,0 +1,30 @@ +/* + * 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.yahoo; + +import java.util.*; + +import net.java.sip.communicator.service.protocol.*; + +/** + * The Yahoo implementation of a sip-communicator AccountID + * + * @author Damian Minkov + */ +public class YahooAccountID + extends AccountID +{ + /** + * Creates an account id from the specified id and account properties. + * @param id the id identifying this account + * @param accountProperties any other properties necessary for the account. + */ + YahooAccountID(String id, Map accountProperties ) + { + super(id, accountProperties, ProtocolNames.YAHOO, "yahoo.com"); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java new file mode 100644 index 0000000..2874504 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java @@ -0,0 +1,115 @@ +/* + * 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.yahoo; + +import java.util.*; + +import org.osgi.framework.*; +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.service.protocol.*; + +/** + * Loads the Yahoo provider factory and registers it with service in the OSGI + * bundle context. + * + * @author Damian Minkov + */ +public class YahooActivator + implements BundleActivator +{ + private ServiceRegistration yahooPpFactoryServReg = null; + private static BundleContext bundleContext = null; + private static ConfigurationService configurationService = null; + + private static ProtocolProviderFactoryYahooImpl yahooProviderFactory = null; + + /** + * Called when this bundle is started so the Framework can perform the + * bundle-specific activities necessary to start this bundle. + * + * @param context The execution context of the bundle being started. + * @throws Exception If this method throws an exception, this bundle is + * marked as stopped and the Framework will remove this bundle's + * listeners, unregister all services registered by this bundle, and + * release all services used by this bundle. + */ + public void start(BundleContext context) throws Exception + { + this.bundleContext = context; + Hashtable hashtable = new Hashtable(); + hashtable.put(ProtocolProviderFactory.PROTOCOL, ProtocolNames.YAHOO); + + yahooProviderFactory = new ProtocolProviderFactoryYahooImpl(); + + //load all yahoo providers + yahooProviderFactory.loadStoredAccounts(); + + //reg the yahoo account man. + yahooPpFactoryServReg = context.registerService( + ProtocolProviderFactory.class.getName(), + yahooProviderFactory, + hashtable); + } + + /** + * Returns a reference to a ConfigurationService implementation currently + * registered in the bundle context or null if no such implementation was + * found. + * + * @return ConfigurationService a currently valid implementation of the + * configuration service. + */ + public static ConfigurationService getConfigurationService() + { + if(configurationService == null) + { + ServiceReference confReference + = bundleContext.getServiceReference( + ConfigurationService.class.getName()); + configurationService + = (ConfigurationService) bundleContext.getService(confReference); + } + return configurationService; + } + + /** + * Returns a reference to the bundle context that we were started with. + * @return a reference to the BundleContext instance that we were started + * witn. + */ + public static BundleContext getBundleContext() + { + return bundleContext; + } + + /** + * Retrurns a reference to the protocol provider factory that we have + * registered. + * @return a reference to the <tt>ProtocolProviderFactoryYahooImpl</tt> + * instance that we have registered from this package. + */ + static ProtocolProviderFactoryYahooImpl getProtocolProviderFactory() + { + return yahooProviderFactory; + } + + /** + * Called when this bundle is stopped so the Framework can perform the + * bundle-specific activities necessary to stop the bundle. + * + * @param context The execution context of the bundle being stopped. + * @throws Exception If this method throws an exception, the bundle is + * still marked as stopped, and the Framework will remove the bundle's + * listeners, unregister all services registered by the bundle, and + * release all services used by the bundle. + */ + public void stop(BundleContext context) throws Exception + { + yahooProviderFactory.stop(); + yahooPpFactoryServReg.unregister(); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java new file mode 100644 index 0000000..be04d71 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java @@ -0,0 +1,28 @@ +/* + * 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.yahoo; + +import java.io.*; +import ymsg.network.*; + +/** + * Extends The Yahoo session to have access to some + * protected functionality + * Not working for now. + * + * @author Damian Minkov + */ +public class YahooSession + extends Session +{ + + public void renameGroup(String oldName, String newName) + throws IOException + { + transmitGroupRename(oldName, newName); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf new file mode 100644 index 0000000..523e8d4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf @@ -0,0 +1,19 @@ +Bundle-Activator: net.java.sip.communicator.impl.protocol.yahoo.YahooActivator +Bundle-Name: Yahoo Protocol Provider Implementation +Bundle-Description: An Yahoo implementation of the Protocol Provider Service. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.osgi.framework, + javax.net.ssl, + javax.swing, + javax.xml.parsers, + javax.naming, + javax.naming.directory, + org.xml.sax, + sun.security.action, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.util, + net.java.sip.communicator.service.configuration.event, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.protocol.yahooconstants, + net.java.sip.communicator.service.protocol.event |