aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/protocol/yahoo
diff options
context:
space:
mode:
authorDamian Minkov <damencho@jitsi.org>2007-01-12 17:56:58 +0000
committerDamian Minkov <damencho@jitsi.org>2007-01-12 17:56:58 +0000
commit7c6ccbd03b54d59913b45202f06bf053fe730f61 (patch)
tree5c1fd10f7382d1b43f2c67029ec0cdbafd260172 /src/net/java/sip/communicator/impl/protocol/yahoo
parent44d48d13eebcb30fbfedd84424c04820aabebba7 (diff)
downloadjitsi-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')
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java29
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java473
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java278
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java147
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java303
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java1066
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java219
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java264
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java494
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java301
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java1027
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java79
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactYahooImpl.java81
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java30
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java115
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java28
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf19
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