aboutsummaryrefslogtreecommitdiffstats
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
parent44d48d13eebcb30fbfedd84424c04820aabebba7 (diff)
downloadjitsi-7c6ccbd03b54d59913b45202f06bf053fe730f61.zip
jitsi-7c6ccbd03b54d59913b45202f06bf053fe730f61.tar.gz
jitsi-7c6ccbd03b54d59913b45202f06bf053fe730f61.tar.bz2
Yahoo Protocol Provider Implementation
-rw-r--r--build.xml42
-rw-r--r--lib/accounts.properties.template32
-rw-r--r--lib/felix.client.run.properties2
-rw-r--r--lib/felix.unit.test.properties2
-rw-r--r--lib/ymsg_network_v0_61.jarbin0 -> 129222 bytes
-rw-r--r--src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/yahoo16x16-connecting.gifbin0 -> 6152 bytes
-rwxr-xr-xsrc/net/java/sip/communicator/impl/gui/utils/Constants.java2
-rw-r--r--src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java5
-rw-r--r--src/net/java/sip/communicator/impl/gui/utils/images.properties1
-rw-r--r--src/net/java/sip/communicator/impl/protocol/msn/ServerStoredContactListMsnImpl.jbx12
-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
-rw-r--r--src/net/java/sip/communicator/plugin/yahooaccregwizz/FirstWizardPage.java262
-rw-r--r--src/net/java/sip/communicator/plugin/yahooaccregwizz/Resources.java81
-rw-r--r--src/net/java/sip/communicator/plugin/yahooaccregwizz/SecondWizardPage.java48
-rw-r--r--src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccRegWizzActivator.java78
-rw-r--r--src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccountRegistration.java72
-rw-r--r--src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccountRegistrationWizard.java177
-rw-r--r--src/net/java/sip/communicator/plugin/yahooaccregwizz/resources.properties9
-rw-r--r--src/net/java/sip/communicator/plugin/yahooaccregwizz/resources/yahoo.pngbin0 -> 3652 bytes
-rw-r--r--src/net/java/sip/communicator/plugin/yahooaccregwizz/yahooaccregwizz.manifest.mf30
-rw-r--r--src/net/java/sip/communicator/service/protocol/ProtocolNames.java2
-rw-r--r--src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf1
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountInstallation.java217
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountUninstallation.java270
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountUninstallationPersistence.java100
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetBasicInstantMessaging.java510
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetPersistentPresence.java559
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetPresence.java989
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetTypingNotifications.java293
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/TestProtocolProviderServiceYahooImpl.java285
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/YahooProtocolProviderServiceLick.java106
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/YahooSlickFixture.java295
-rw-r--r--test/net/java/sip/communicator/slick/protocol/yahoo/yahoo.provider.slick.manifest.mf15
49 files changed, 9432 insertions, 18 deletions
diff --git a/build.xml b/build.xml
index 776b9f7..13a4dd7 100644
--- a/build.xml
+++ b/build.xml
@@ -332,7 +332,7 @@
</condition>
<!-- Prepare the logging.properties file for macosx -->
- <exec executable="/usr/bin/sed"
+ <exec executable="/usr/bin/sed"
dir="${basedir}"
input="${inst.resrc}/logging.properties"
output="${macosx.resrc.dir}/logging.properties">
@@ -393,7 +393,7 @@
<include name="${bundles.dest.macosx}/*.jar" />
<exclude name="${bundles.dest}/*-slick.jar" />
</jarfileset>
- <javafilelist dir="${macosx.resrc.dir}"
+ <javafilelist dir="${macosx.resrc.dir}"
files="logging.properties"/>
<javafilelist dir="${macosx.resrc.dir}"
files="felix.client.run.properties"/>
@@ -639,7 +639,7 @@
<sysproperty key="java.util.logging.config.file"
value="lib/logging.properties"/>
- <sysproperty key="net.java.preferIPv6Addresses"
+ <sysproperty key="java.net.preferIPv6Addresses"
value="true"/>
<!-- Setting properties necessary for dependencies on native libs.-->
@@ -719,10 +719,11 @@
bundle-fileaccess-slick,bundle-media,bundle-media-slick,
bundle-protocol,bundle-icq,bundle-icq-slick,bundle-mock,
bundle-jabber,bundle-jabber-slick,bundle-swing-ui,
- bundle-msn,bundle-msn-slick,
+ bundle-msn,bundle-msn-slick,bundle-yahoo,bundle-yahoo-slick,
bundle-contactlist,meta-contactlist,meta-contactlist-slick,
bundle-plugin-icqaccregwizz,bundle-plugin-jabberaccregwizz,
bundle-plugin-msnaccregwizz,bundle-plugin-sipaccregwizz,
+ bundle-plugin-yahooaccregwizz,
bundle-version,bundle-version-impl,bundle-shutdown,
bundle-growlnotification"/>
@@ -1068,6 +1069,29 @@ javax.swing.event, javax.swing.border"/>
<zipfileset src="${lib}/commons-logging.jar" prefix=""/>
</jar>
</target>
+
+ <!-- BUNDLE-YAHOO -->
+ <target name="bundle-yahoo">
+ <!-- Creates a bundle containing the yahoo impl of the protocol provider.-->
+ <jar compress="false" destfile="${bundles.dest}/protocol-yahoo.jar"
+ manifest="src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf">
+ <zipfileset dir="${dest}/net/java/sip/communicator/impl/protocol/yahoo"
+ prefix="net/java/sip/communicator/impl/protocol/yahoo"/>
+ <zipfileset src="${lib}/ymsg_network_v0_61.jar" prefix=""/>
+ </jar>
+ </target>
+
+ <!-- BUNDLE-YAHOO-SLICK -->
+ <!-- Creates a bundle containing the slick for the Yahoo protocol provider.-->
+ <target name="bundle-yahoo-slick">
+
+ <jar compress="false" destfile="${bundles.dest}/protocol-yahoo-slick.jar"
+ manifest="test/net/java/sip/communicator/slick/protocol/yahoo/yahoo.provider.slick.manifest.mf">
+ <zipfileset dir="${dest}/net/java/sip/communicator/slick/protocol/yahoo"
+ prefix="net/java/sip/communicator/slick/protocol/yahoo"/>
+ <zipfileset src="${lib}/ymsg_network_v0_61.jar" prefix=""/>
+ </jar>
+ </target>
<!-- BUNDLE-SWING-UI -->
@@ -1141,6 +1165,16 @@ javax.swing.event, javax.swing.border"/>
prefix="net/java/sip/communicator/plugin/msnaccregwizz"/>
</jar>
</target>
+
+ <!-- BUNDLE-PLUGIN-YAHOOACCREGWIZZ -->
+ <target name="bundle-plugin-yahooaccregwizz">
+ <!-- Creates a bundle for the plugin Yahoo Account Registration Wizard.-->
+ <jar compress="false" destfile="${bundles.dest}/yahooaccregwizz.jar"
+ manifest="src/net/java/sip/communicator/plugin/yahooaccregwizz/yahooaccregwizz.manifest.mf">
+ <zipfileset dir="${dest}/net/java/sip/communicator/plugin/yahooaccregwizz"
+ prefix="net/java/sip/communicator/plugin/yahooaccregwizz"/>
+ </jar>
+ </target>
<!-- BUNDLE-PLUGIN-SIPACCREGWIZZ -->
<target name="bundle-plugin-sipaccregwizz">
diff --git a/lib/accounts.properties.template b/lib/accounts.properties.template
index 862bd50..ac439c3 100644
--- a/lib/accounts.properties.template
+++ b/lib/accounts.properties.template
@@ -185,3 +185,35 @@ accounts.msn.CONTACT_LIST=
# list of msn accounts to notify during testing (optional)
accounts.reporting.MSN_REPORT_LIST=
+
+# YAHOO PROPERTIES
+
+# YAHOO ACCOUNT 1
+# The username needed to log onto the server
+accounts.yahoo.account1.USER_ID=
+
+# The password (in plain text) needed to log the user specified in USER_ID
+# on the server
+accounts.yahoo.account1.PASSWORD=
+
+# YAHOO ACCOUNT 2
+
+# The username needed to log onto the server
+accounts.yahoo.account2.USER_ID=
+
+# The password (in plain text) needed to log the user specified in USER_ID
+# on the server
+accounts.yahoo.account2.PASSWORD=
+
+# The following describes the contact list that we will store on the Msn
+# server for the tested account implementation. The value of the CONTACT_LIST
+# property must be a space separated string containing elements each of which
+# is in the format: GroupName.UIN
+#
+#
+# VERY IMPORTANT!!! All other users in this contact list will be removed and
+# the contacts and contact groups enumerated beneath will be added instead
+accounts.yahoo.CONTACT_LIST=
+
+# list of yahoo accounts to notify during testing (optional)
+accounts.reporting.YAHOO_REPORT_LIST=
diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties
index 467ff42..139279d 100644
--- a/lib/felix.client.run.properties
+++ b/lib/felix.client.run.properties
@@ -57,6 +57,7 @@ felix.auto.start.3= \
reference:file:sc-bundles/protocol-sip.jar \
reference:file:sc-bundles/protocol-jabber.jar \
reference:file:sc-bundles/protocol-msn.jar \
+ reference:file:sc-bundles/protocol-yahoo.jar \
reference:file:sc-bundles/netaddr.jar \
reference:file:sc-bundles/meta-cl.jar
@@ -74,6 +75,7 @@ felix.auto.start.4= \
reference:file:sc-bundles/sipaccregwizz.jar \
reference:file:sc-bundles/jabberaccregwizz.jar \
reference:file:sc-bundles/msnaccregwizz.jar \
+ reference:file:sc-bundles/yahooaccregwizz.jar \
reference:file:sc-bundles/shutdown.jar
# Uncomment the following lines if you want to run the architect viewer
diff --git a/lib/felix.unit.test.properties b/lib/felix.unit.test.properties
index 0cdbcd6..101eb43 100644
--- a/lib/felix.unit.test.properties
+++ b/lib/felix.unit.test.properties
@@ -57,6 +57,7 @@ felix.auto.start.4= \
file:sc-bundles/protocol-jabber.jar \
file:sc-bundles/protocol-msn.jar \
file:sc-bundles/protocol-sip.jar \
+ file:sc-bundles/protocol-yahoo.jar \
file:sc-bundles/media.jar \
file:sc-bundles/meta-cl.jar \
file:sc-bundles/msghistory.jar \
@@ -76,6 +77,7 @@ felix.auto.start.5= \
file:sc-bundles/protocol-sip-slick.jar \
file:sc-bundles/protocol-jabber-slick.jar \
file:sc-bundles/protocol-msn-slick.jar \
+ file:sc-bundles/protocol-yahoo-slick.jar \
file:sc-bundles/msghistory-slick.jar \
file:sc-bundles/callhistory-slick.jar
diff --git a/lib/ymsg_network_v0_61.jar b/lib/ymsg_network_v0_61.jar
new file mode 100644
index 0000000..c5e3df7
--- /dev/null
+++ b/lib/ymsg_network_v0_61.jar
Binary files differ
diff --git a/src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/yahoo16x16-connecting.gif b/src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/yahoo16x16-connecting.gif
new file mode 100644
index 0000000..4f07cb7
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/resources/protocols/yahoo/yahoo16x16-connecting.gif
Binary files differ
diff --git a/src/net/java/sip/communicator/impl/gui/utils/Constants.java b/src/net/java/sip/communicator/impl/gui/utils/Constants.java
index 8cda9b4..3b9b706 100755
--- a/src/net/java/sip/communicator/impl/gui/utils/Constants.java
+++ b/src/net/java/sip/communicator/impl/gui/utils/Constants.java
@@ -461,7 +461,7 @@ public class Constants {
}
else if (protocolName.equals(Constants.YAHOO)) {
return ImageLoader.getAnimatedImage(
- ImageLoader.ICQ_CONNECTING);
+ ImageLoader.YAHOO_CONNECTING);
}
else if (protocolName.equals(Constants.JABBER)) {
return ImageLoader.getAnimatedImage(
diff --git a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
index c82e869..bd70484 100644
--- a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
+++ b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
@@ -802,6 +802,11 @@ public class ImageLoader {
* The Yahoo logo 16x16 icon.
*/
public static final ImageID YAHOO_LOGO = new ImageID("YAHOO_LOGO");
+
+ /**
+ * The ICQ "connecting" 16x16 animated icon.
+ */
+ public static final ImageID YAHOO_CONNECTING = new ImageID("YAHOO_CONNECTING");
/**
* The Jabber logo 32x32 icon.
diff --git a/src/net/java/sip/communicator/impl/gui/utils/images.properties b/src/net/java/sip/communicator/impl/gui/utils/images.properties
index c84f346..5009060 100644
--- a/src/net/java/sip/communicator/impl/gui/utils/images.properties
+++ b/src/net/java/sip/communicator/impl/gui/utils/images.properties
@@ -109,6 +109,7 @@ AIM_32x32=net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim.png
AIM_LOGO=net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim16.png
YAHOO_32x32=net/java/sip/communicator/impl/gui/resources/protocols/yahoo/Yahoo.png
YAHOO_LOGO=net/java/sip/communicator/impl/gui/resources/protocols/yahoo/Yahoo16.png
+YAHOO_CONNECTING=net/java/sip/communicator/impl/gui/resources/protocols/yahoo/yahoo16x16-connecting.gif
JABBER_32x32=net/java/sip/communicator/impl/gui/resources/protocols/jabber/Jabber2.png
JABBER_CONNECTING=net/java/sip/communicator/impl/gui/resources/protocols/jabber/jabber16x16-connecting.gif
JABBER_LOGO=net/java/sip/communicator/impl/gui/resources/protocols/jabber/Jabber16.png
diff --git a/src/net/java/sip/communicator/impl/protocol/msn/ServerStoredContactListMsnImpl.jbx b/src/net/java/sip/communicator/impl/protocol/msn/ServerStoredContactListMsnImpl.jbx
deleted file mode 100644
index a6a1c55..0000000
--- a/src/net/java/sip/communicator/impl/protocol/msn/ServerStoredContactListMsnImpl.jbx
+++ /dev/null
@@ -1,12 +0,0 @@
-[PropertyInfo]
-contactListModManager,net.java.sip.communicator.impl.protocol.msn.EventManager,false,false, , ,false,<default>
-messenger,net.sf.jml.MsnMessenger,false,false, , ,false,<default>
-msnProvider,net.java.sip.communicator.impl.protocol.msn.ProtocolProviderServiceMsnImpl,false,false, , ,false,<default>
-parentOperationSet,net.java.sip.communicator.impl.protocol.msn.OperationSetPersistentPresenceMsnImpl,false,false, , ,false,<default>
-rootGroup,net.java.sip.communicator.service.protocol.ContactGroup,false,false, , ,true,<default>
-serverStoredGroupListeners,java.util.Vector,false,false, , ,false,<default>
-[IconNames]
-
-
-
-
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
diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/yahooaccregwizz/FirstWizardPage.java
new file mode 100644
index 0000000..007a26f
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/FirstWizardPage.java
@@ -0,0 +1,262 @@
+/*
+ * 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.plugin.yahooaccregwizz;
+
+import java.awt.*;
+import java.util.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * The <tt>FirstWizardPage</tt> is the page, where user could enter the uin
+ * and the password of the account.
+ *
+ * @author Yana Stamcheva
+ * @author Damian Minkov
+ */
+public class FirstWizardPage extends JPanel
+ implements WizardPage, DocumentListener {
+
+ public static final String FIRST_PAGE_IDENTIFIER = "FirstPageIdentifier";
+
+ private JPanel uinPassPanel = new JPanel(new BorderLayout(10, 10));
+
+ private JPanel labelsPanel = new JPanel();
+
+ private JPanel valuesPanel = new JPanel();
+
+ private JLabel uinLabel = new JLabel(Resources.getString("uin"));
+
+ private JLabel passLabel = new JLabel(Resources.getString("password"));
+
+ private JLabel existingAccountLabel
+ = new JLabel(Resources.getString("existingAccount"));
+
+ private JPanel emptyPanel = new JPanel();
+
+ private JLabel uinExampleLabel = new JLabel("Ex: johnsmith@hotmail.com");
+
+ private JTextField uinField = new JTextField();
+
+ private JPasswordField passField = new JPasswordField();
+
+ private JCheckBox rememberPassBox = new JCheckBox(
+ Resources.getString("rememberPassword"));
+
+ private JPanel mainPanel = new JPanel();
+
+ private Object nextPageIdentifier = WizardPage.SUMMARY_PAGE_IDENTIFIER;
+
+ private YahooAccountRegistration registration;
+
+ private WizardContainer wizardContainer;
+
+ /**
+ * Creates an instance of <tt>FirstWizardPage</tt>.
+ * @param registration the <tt>YahooAccountRegistration</tt>, where
+ * all data through the wizard are stored
+ * @param wizardContainer the wizardContainer, where this page will
+ * be added
+ */
+ public FirstWizardPage(YahooAccountRegistration registration,
+ WizardContainer wizardContainer) {
+
+ super(new BorderLayout());
+
+ this.wizardContainer = wizardContainer;
+
+ this.registration = registration;
+
+ this.setPreferredSize(new Dimension(300, 150));
+
+ mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
+
+ this.init();
+
+ this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ this.labelsPanel.setLayout(new BoxLayout(labelsPanel, BoxLayout.Y_AXIS));
+
+ this.valuesPanel.setLayout(new BoxLayout(valuesPanel, BoxLayout.Y_AXIS));
+ }
+
+ /**
+ * Initializes all panels, buttons, etc.
+ */
+ private void init() {
+ this.uinField.getDocument().addDocumentListener(this);
+ this.rememberPassBox.setSelected(true);
+
+ this.existingAccountLabel.setForeground(Color.RED);
+
+ this.uinExampleLabel.setForeground(Color.GRAY);
+ this.uinExampleLabel.setFont(uinExampleLabel.getFont().deriveFont(8));
+ this.emptyPanel.setMaximumSize(new Dimension(40, 35));
+ this.uinExampleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0));
+
+ labelsPanel.add(uinLabel);
+ labelsPanel.add(emptyPanel);
+ labelsPanel.add(passLabel);
+
+ valuesPanel.add(uinField);
+ valuesPanel.add(uinExampleLabel);
+ valuesPanel.add(passField);
+
+ uinPassPanel.add(labelsPanel, BorderLayout.WEST);
+ uinPassPanel.add(valuesPanel, BorderLayout.CENTER);
+ uinPassPanel.add(rememberPassBox, BorderLayout.SOUTH);
+
+ uinPassPanel.setBorder(BorderFactory
+ .createTitledBorder(Resources.getString("uinAndPassword")));
+
+ mainPanel.add(uinPassPanel);
+ this.add(mainPanel, BorderLayout.NORTH);
+ }
+
+ /**
+ * Implements the <code>WizardPage.getIdentifier</code> to return
+ * this page identifier.
+ */
+ public Object getIdentifier() {
+ return FIRST_PAGE_IDENTIFIER;
+ }
+
+ /**
+ * Implements the <code>WizardPage.getNextPageIdentifier</code> to return
+ * the next page identifier - the summary page.
+ */
+ public Object getNextPageIdentifier() {
+ return nextPageIdentifier;
+ }
+
+ /**
+ * Implements the <code>WizardPage.getBackPageIdentifier</code> to return
+ * the next back identifier - the default page.
+ */
+ public Object getBackPageIdentifier() {
+ return WizardPage.DEFAULT_PAGE_IDENTIFIER;
+ }
+
+ /**
+ * Implements the <code>WizardPage.getWizardForm</code> to return
+ * this panel.
+ */
+ public Object getWizardForm() {
+ return this;
+ }
+
+ /**
+ * Before this page is displayed enables or disables the "Next" wizard
+ * button according to whether the UIN field is empty.
+ */
+ public void pageShowing() {
+ this.setNextButtonAccordingToUIN();
+ }
+
+ /**
+ * Saves the user input when the "Next" wizard buttons is clicked.
+ */
+ public void pageNext() {
+ String uin = uinField.getText();
+
+ if(isExistingAccount(uin)) {
+ nextPageIdentifier = FIRST_PAGE_IDENTIFIER;
+ uinPassPanel.add(existingAccountLabel, BorderLayout.NORTH);
+ this.revalidate();
+ }
+ else {
+ nextPageIdentifier = SUMMARY_PAGE_IDENTIFIER;
+ uinPassPanel.remove(existingAccountLabel);
+
+ registration.setUin(uinField.getText());
+ registration.setPassword(new String(passField.getPassword()));
+ registration.setRememberPassword(rememberPassBox.isSelected());
+ }
+ }
+
+ /**
+ * Enables or disables the "Next" wizard button according to whether the
+ * UIN field is empty.
+ */
+ private void setNextButtonAccordingToUIN() {
+ if (uinField.getText() == null || uinField.getText().equals("")) {
+ wizardContainer.setNextFinishButtonEnabled(false);
+ }
+ else {
+ wizardContainer.setNextFinishButtonEnabled(true);
+ }
+ }
+
+ /**
+ * Handles the <tt>DocumentEvent</tt> triggered when user types in the
+ * UIN field. Enables or disables the "Next" wizard button according to
+ * whether the UIN field is empty.
+ */
+ public void insertUpdate(DocumentEvent e) {
+ this.setNextButtonAccordingToUIN();
+ }
+
+ /**
+ * Handles the <tt>DocumentEvent</tt> triggered when user deletes letters
+ * from the UIN field. Enables or disables the "Next" wizard button
+ * according to whether the UIN field is empty.
+ */
+ public void removeUpdate(DocumentEvent e) {
+ this.setNextButtonAccordingToUIN();
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ }
+
+ public void pageHiding() {
+ }
+
+ public void pageShown() {
+ }
+
+ public void pageBack() {
+ }
+
+ /**
+ * Fills the UIN and Password fields in this panel with the data comming
+ * from the given protocolProvider.
+ * @param protocolProvider The <tt>ProtocolProviderService</tt> to load the
+ * data from.
+ */
+ public void loadAccount(ProtocolProviderService protocolProvider) {
+ AccountID accountID = protocolProvider.getAccountID();
+ String password = (String)accountID.getAccountProperties()
+ .get(ProtocolProviderFactory.PASSWORD);
+
+ this.uinField.setText(accountID.getUserID());
+
+ if(password != null) {
+ this.passField.setText(password);
+ this.rememberPassBox.setSelected(true);
+ }
+ }
+
+ private boolean isExistingAccount(String accountName)
+ {
+ ProtocolProviderFactory factory
+ = YahooAccRegWizzActivator.getYahooProtocolProviderFactory();
+
+ ArrayList registeredAccounts = factory.getRegisteredAccounts();
+
+ for(int i = 0; i < registeredAccounts.size(); i ++) {
+ AccountID accountID = (AccountID) registeredAccounts.get(i);
+
+ if(accountName.equalsIgnoreCase(accountID.getUserID()))
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/Resources.java b/src/net/java/sip/communicator/plugin/yahooaccregwizz/Resources.java
new file mode 100644
index 0000000..96001e6
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/Resources.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.plugin.yahooaccregwizz;
+
+import java.io.*;
+import java.util.*;
+
+import net.java.sip.communicator.util.*;
+/**
+ * The Messages class manages the access to the internationalization
+ * properties files.
+ * @author Yana Stamcheva
+ */
+public class Resources {
+
+ private static Logger log = Logger.getLogger(Resources.class);
+
+ private static final String BUNDLE_NAME
+ = "net.java.sip.communicator.plugin.yahooaccregwizz.resources";
+
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
+ .getBundle(BUNDLE_NAME);
+
+ public static ImageID YAHOO_LOGO = new ImageID("protocolIcon");
+
+ /**
+ * Returns an internationalized string corresponding to the given key.
+ * @param key The key of the string.
+ * @return An internationalized string corresponding to the given key.
+ */
+ public static String getString(String key) {
+ try {
+ return RESOURCE_BUNDLE.getString(key);
+
+ } catch (MissingResourceException e) {
+
+ return '!' + key + '!';
+ }
+ }
+
+ /**
+ * Loads an image from a given image identifier.
+ * @param imageID The identifier of the image.
+ * @return The image for the given identifier.
+ */
+ public static byte[] getImage(ImageID imageID) {
+ byte[] image = new byte[100000];
+
+ String path = Resources.getString(imageID.getId());
+ try {
+ Resources.class.getClassLoader()
+ .getResourceAsStream(path).read(image);
+
+ } catch (IOException e) {
+ log.error("Failed to load image:" + path, e);
+ }
+
+ return image;
+ }
+
+ /**
+ * Represents the Image Identifier.
+ */
+ public static class ImageID {
+ private String id;
+
+ private ImageID(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+ }
+
+}
diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/SecondWizardPage.java b/src/net/java/sip/communicator/plugin/yahooaccregwizz/SecondWizardPage.java
new file mode 100644
index 0000000..c5c537a
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/SecondWizardPage.java
@@ -0,0 +1,48 @@
+/*
+ * 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.plugin.yahooaccregwizz;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.service.gui.*;
+
+public class SecondWizardPage extends JPanel
+ implements WizardPage {
+
+ public static final String SECOND_PAGE_IDENTIFIER = "SecondPageIdentifier";
+
+ public Object getIdentifier() {
+ return SECOND_PAGE_IDENTIFIER;
+ }
+
+ public Object getNextPageIdentifier() {
+ return FINISH_PAGE_IDENTIFIER;
+ }
+
+ public Object getBackPageIdentifier() {
+ return FirstWizardPage.FIRST_PAGE_IDENTIFIER;
+ }
+
+ public Object getWizardForm() {
+ return this;
+ }
+
+ public void pageHiding() {
+ }
+
+ public void pageShown() {
+ }
+
+ public void pageShowing() {
+ }
+
+ public void pageNext() {
+ }
+
+ public void pageBack() {
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccRegWizzActivator.java b/src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccRegWizzActivator.java
new file mode 100644
index 0000000..fe35f38
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccRegWizzActivator.java
@@ -0,0 +1,78 @@
+/*
+ * 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.plugin.yahooaccregwizz;
+
+import org.osgi.framework.*;
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Registers the <tt>YahooAccountRegistrationWizard</tt> in the UI Service.
+ *
+ * @author Yana Stamcheva
+ */
+public class YahooAccRegWizzActivator implements BundleActivator {
+
+ public static BundleContext bundleContext;
+
+ private static Logger logger = Logger.getLogger(
+ YahooAccRegWizzActivator.class.getName());
+
+ private static ConfigurationService configService;
+
+ /**
+ * Starts this bundle.
+ * @param bc BundleContext
+ * @throws Exception
+ */
+ public void start(BundleContext bc) throws Exception {
+
+ bundleContext = bc;
+
+ ServiceReference uiServiceRef = bundleContext
+ .getServiceReference(UIService.class.getName());
+
+ UIService uiService
+ = (UIService) bundleContext.getService(uiServiceRef);
+
+ AccountRegistrationWizardContainer wizardContainer
+ = uiService.getAccountRegWizardContainer();
+
+ YahooAccountRegistrationWizard yahooWizard
+ = new YahooAccountRegistrationWizard(wizardContainer);
+
+ wizardContainer.addAccountRegistrationWizard(yahooWizard);
+ }
+
+ public void stop(BundleContext bundleContext) throws Exception {
+ }
+
+ /**
+ * Returns the <tt>ProtocolProviderFactory</tt> for the Yahoo protocol.
+ * @return the <tt>ProtocolProviderFactory</tt> for the Yahoo protocol
+ */
+ public static ProtocolProviderFactory getYahooProtocolProviderFactory() {
+
+ ServiceReference[] serRefs = null;
+
+ String osgiFilter = "("
+ + ProtocolProviderFactory.PROTOCOL
+ + "="+ProtocolNames.YAHOO+")";
+
+ try {
+ serRefs = bundleContext.getServiceReferences(
+ ProtocolProviderFactory.class.getName(), osgiFilter);
+ }
+ catch (InvalidSyntaxException ex){
+ logger.error("YahooAccRegWizzActivator : " + ex);
+ }
+
+ return (ProtocolProviderFactory) bundleContext.getService(serRefs[0]);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccountRegistration.java b/src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccountRegistration.java
new file mode 100644
index 0000000..a93e3b5
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccountRegistration.java
@@ -0,0 +1,72 @@
+/*
+ * 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.plugin.yahooaccregwizz;
+
+/**
+ * The <tt>YahooAccountRegistration</tt> is used to store all user input data
+ * through the <tt>YahooAccountRegistrationWizard</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public class YahooAccountRegistration {
+
+ private String uin;
+
+ private String password;
+
+ private boolean rememberPassword;
+
+ /**
+ * Returns the password of the yahoo registration account.
+ * @return the password of the yahoo registration account.
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * Sets the password of the yahoo registration account.
+ * @param password the password of the yahoo registration account.
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * Returns TRUE if password has to remembered, FALSE otherwise.
+ * @return TRUE if password has to remembered, FALSE otherwise
+ */
+ public boolean isRememberPassword() {
+ return rememberPassword;
+ }
+
+ /**
+ * Sets the rememberPassword value of this yahoo account registration.
+ * @param rememberPassword TRUE if password has to remembered, FALSE
+ * otherwise
+ */
+ public void setRememberPassword(boolean rememberPassword) {
+ this.rememberPassword = rememberPassword;
+ }
+
+ /**
+ * Returns the UIN of the yahoo registration account.
+ * @return the UIN of the yahoo registration account.
+ */
+ public String getUin() {
+ return uin;
+ }
+
+ /**
+ * Sets the UIN of the yahoo registration account.
+ * @param uin the UIN of the yahoo registration account.
+ */
+ public void setUin(String uin) {
+ this.uin = uin;
+ }
+
+}
diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccountRegistrationWizard.java b/src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccountRegistrationWizard.java
new file mode 100644
index 0000000..45bff02
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/YahooAccountRegistrationWizard.java
@@ -0,0 +1,177 @@
+/*
+ * 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.plugin.yahooaccregwizz;
+
+import java.util.*;
+
+import javax.swing.*;
+
+import org.osgi.framework.*;
+
+import net.java.sip.communicator.impl.gui.customcontrols.*;
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * The <tt>YahooAccountRegistrationWizard</tt> is an implementation of the
+ * <tt>AccountRegistrationWizard</tt> for the Yahoo protocol. It should allow
+ * the user to create and configure a new Yahoo account.
+ *
+ * @author Yana Stamcheva
+ */
+public class YahooAccountRegistrationWizard implements AccountRegistrationWizard {
+
+ private FirstWizardPage firstWizardPage;
+
+ private YahooAccountRegistration registration
+ = new YahooAccountRegistration();
+
+ private WizardContainer wizardContainer;
+
+ private ProtocolProviderService protocolProvider;
+
+ private String propertiesPackage = "net.java.sip.communicator.plugin.yahooaccregwizz";
+
+ private boolean isModification;
+
+ /**
+ * Creates an instance of <tt>YahooAccountRegistrationWizard</tt>.
+ * @param wizardContainer the wizard container, where this wizard
+ * is added
+ */
+ public YahooAccountRegistrationWizard(WizardContainer wizardContainer) {
+ this.wizardContainer = wizardContainer;
+ }
+
+ /**
+ * Implements the <code>AccountRegistrationWizard.getIcon</code> method.
+ * Returns the icon to be used for this wizard.
+ * @return byte[]
+ */
+ public byte[] getIcon() {
+ return Resources.getImage(Resources.YAHOO_LOGO);
+ }
+
+ /**
+ * Implements the <code>AccountRegistrationWizard.getProtocolName</code>
+ * method. Returns the protocol name for this wizard.
+ * @return String
+ */
+ public String getProtocolName() {
+ return Resources.getString("protocolName");
+ }
+
+ /**
+ * Implements the <code>AccountRegistrationWizard.getProtocolDescription
+ * </code> method. Returns the description of the protocol for this wizard.
+ * @return String
+ */
+ public String getProtocolDescription() {
+ return Resources.getString("protocolDescription");
+ }
+
+ /**
+ * Returns the set of pages contained in this wizard.
+ * @return Iterator
+ */
+ public Iterator getPages() {
+ ArrayList pages = new ArrayList();
+ firstWizardPage = new FirstWizardPage(registration, wizardContainer);
+
+ pages.add(firstWizardPage);
+
+ return pages.iterator();
+ }
+
+ /**
+ * Returns the set of data that user has entered through this wizard.
+ * @return Iterator
+ */
+ public Iterator getSummary() {
+ Hashtable summaryTable = new Hashtable();
+
+ summaryTable.put("UIN", registration.getUin());
+ summaryTable.put("Remember password",
+ new Boolean(registration.isRememberPassword()));
+
+ return summaryTable.entrySet().iterator();
+ }
+
+ /**
+ * Installs the account created through this wizard.
+ * @return ProtocolProviderService
+ */
+ public ProtocolProviderService finish() {
+ firstWizardPage = null;
+ ProtocolProviderFactory factory
+ = YahooAccRegWizzActivator.getYahooProtocolProviderFactory();
+
+ return this.installAccount(factory,
+ registration.getUin(), registration.getPassword());
+ }
+
+ /**
+ * Creates an account for the given user and password.
+ * @param providerFactory the ProtocolProviderFactory which will create
+ * the account
+ * @param user the user identifier
+ * @param passwd the password
+ * @return the <tt>ProtocolProviderService</tt> for the new account.
+ */
+ public ProtocolProviderService installAccount(
+ ProtocolProviderFactory providerFactory,
+ String user,
+ String passwd) {
+
+ Hashtable accountProperties = new Hashtable();
+
+ if(registration.isRememberPassword()) {
+ accountProperties.put(ProtocolProviderFactory.PASSWORD, passwd);
+ }
+
+ if(isModification) {
+ providerFactory.uninstallAccount(protocolProvider.getAccountID());
+ this.protocolProvider = null;
+ }
+
+ try {
+ AccountID accountID = providerFactory.installAccount(
+ user, accountProperties);
+
+ ServiceReference serRef = providerFactory
+ .getProviderForAccount(accountID);
+
+ protocolProvider
+ = (ProtocolProviderService) YahooAccRegWizzActivator.bundleContext
+ .getService(serRef);
+ }
+ catch (IllegalArgumentException e) {
+ new ErrorDialog(null, e.getMessage()).showDialog();
+ }
+ catch (IllegalStateException e) {
+ new ErrorDialog(null, e.getMessage()).showDialog();
+ }
+
+ return protocolProvider;
+ }
+
+ /**
+ * Fills the UIN and Password fields in this panel with the data comming
+ * from the given protocolProvider.
+ * @param protocolProvider The <tt>ProtocolProviderService</tt> to load the
+ * data from.
+ */
+ public void loadAccount(ProtocolProviderService protocolProvider) {
+
+ this.protocolProvider = protocolProvider;
+
+ this.firstWizardPage.loadAccount(protocolProvider);
+
+ this.isModification = true;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/resources.properties b/src/net/java/sip/communicator/plugin/yahooaccregwizz/resources.properties
new file mode 100644
index 0000000..149fdde
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/resources.properties
@@ -0,0 +1,9 @@
+protocolName=YAHOO
+protocolDescription=A protocol to connect and chat on the Yahoo Service.
+uin=Username:
+password=Password:
+rememberPassword=Remember password
+uinAndPassword=ID and Password
+existingAccount=* The account you entered is already installed.
+
+protocolIcon=net/java/sip/communicator/plugin/yahooaccregwizz/resources/yahoo.png
diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/resources/yahoo.png b/src/net/java/sip/communicator/plugin/yahooaccregwizz/resources/yahoo.png
new file mode 100644
index 0000000..feaf973
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/resources/yahoo.png
Binary files differ
diff --git a/src/net/java/sip/communicator/plugin/yahooaccregwizz/yahooaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/yahooaccregwizz/yahooaccregwizz.manifest.mf
new file mode 100644
index 0000000..787f3e2
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/yahooaccregwizz/yahooaccregwizz.manifest.mf
@@ -0,0 +1,30 @@
+Bundle-Activator: net.java.sip.communicator.plugin.yahooaccregwizz.YahooAccRegWizzActivator
+Bundle-Name: Yahoo account registration wizard
+Bundle-Description: Yahoo account registration wizard.
+Bundle-Vendor: sip-communicator.org
+Bundle-Version: 0.0.1
+Import-Package: org.osgi.framework,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.service.configuration,
+ net.java.sip.communicator.service.configuration.event,
+ net.java.sip.communicator.service.protocol,
+ net.java.sip.communicator.service.protocol.icqconstants,
+ net.java.sip.communicator.service.protocol.event,
+ net.java.sip.communicator.service.contactlist,
+ net.java.sip.communicator.service.contactlist.event,
+ net.java.sip.communicator.service.gui,
+ net.java.sip.communicator.service.gui.event,
+ javax.swing,
+ javax.swing.event,
+ javax.swing.table,
+ javax.swing.text,
+ javax.swing.text.html,
+ javax.accessibility,
+ javax.swing.plaf,
+ javax.swing.plaf.metal,
+ javax.swing.plaf.basic,
+ javax.imageio,
+ javax.swing.filechooser,
+ javax.swing.tree,
+ javax.swing.undo,
+ javax.swing.border
diff --git a/src/net/java/sip/communicator/service/protocol/ProtocolNames.java b/src/net/java/sip/communicator/service/protocol/ProtocolNames.java
index 1c76f69..71bddd8 100644
--- a/src/net/java/sip/communicator/service/protocol/ProtocolNames.java
+++ b/src/net/java/sip/communicator/service/protocol/ProtocolNames.java
@@ -54,7 +54,7 @@ public interface ProtocolNames
/**
* The Yahoo! messenger protcool.
*/
- public static final String YAHOO = "Yahoo!";
+ public static final String YAHOO = "Yahoo";
/**
* The Skype protcool.
diff --git a/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf b/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf
index 685c671..0ba2dfe 100644
--- a/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf
+++ b/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf
@@ -10,4 +10,5 @@ Export-Package: net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,
net.java.sip.communicator.service.protocol.jabberconstants,
net.java.sip.communicator.service.protocol.msnconstants,
+ net.java.sip.communicator.service.protocol.yahooconstants,
net.java.sip.communicator.service.protocol.event
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountInstallation.java b/test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountInstallation.java
new file mode 100644
index 0000000..68f6efd
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountInstallation.java
@@ -0,0 +1,217 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import java.util.*;
+
+import org.osgi.framework.*;
+import junit.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+public class TestAccountInstallation
+ extends TestCase
+{
+ private static final Logger logger =
+ Logger.getLogger(TestAccountInstallation.class);
+
+ /**
+ * Creates the test with the specified method name.
+ * @param name the name of the method to execute.
+ */
+ public TestAccountInstallation(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * JUnit setup method.
+ * @throws Exception in case anything goes wrong.
+ */
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ /**
+ * JUnit teardown method.
+ * @throws Exception in case anything goes wrong.
+ */
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ /**
+ * Installs an account and verifies whether the installation has gone well.
+ */
+ public void testInstallAccount()
+ {
+ // first obtain a reference to the provider factory
+ ServiceReference[] serRefs = null;
+ String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL
+ + "="+ProtocolNames.YAHOO+")";
+ try{
+ serRefs = YahooSlickFixture.bc.getServiceReferences(
+ ProtocolProviderFactory.class.getName(), osgiFilter);
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ //this really shouldhn't occur as the filter expression is static.
+ fail(osgiFilter + " is not a valid osgi filter");
+ }
+
+ assertTrue(
+ "Failed to find a provider factory service for protocol Yahoo",
+ serRefs != null && serRefs.length > 0);
+
+ //Keep the reference for later usage.
+ ProtocolProviderFactory yahooProviderFactory = (ProtocolProviderFactory)
+ YahooSlickFixture.bc.getService(serRefs[0]);
+
+ //make sure the account is empty
+ assertTrue("There was an account registered with the account mananger "
+ +"before we've installed any",
+ yahooProviderFactory.getRegisteredAccounts().size() == 0);
+
+
+ //Prepare the properties of the first yahoo account.
+
+ Hashtable yahooAccount1Properties = getAccountProperties(
+ YahooProtocolProviderServiceLick.ACCOUNT_1_PREFIX);
+ Hashtable yahooAccount2Properties = getAccountProperties(
+ YahooProtocolProviderServiceLick.ACCOUNT_2_PREFIX);
+
+ //try to install an account with a null account id
+ try{
+ yahooProviderFactory.installAccount(
+ null, yahooAccount1Properties);
+ fail("installing an account with a null account id must result "
+ +"in a NullPointerException");
+ }catch(NullPointerException exc)
+ {
+ //that's what had to happen
+ }
+
+ //now really install the accounts
+ yahooProviderFactory.installAccount(
+ (String)yahooAccount1Properties.get(ProtocolProviderFactory.USER_ID)
+ , yahooAccount1Properties);
+ yahooProviderFactory.installAccount(
+ (String)yahooAccount2Properties.get(ProtocolProviderFactory.USER_ID)
+ , yahooAccount2Properties);
+
+
+ //try to install one of the accounts one more time and verify that an
+ //excepion is thrown.
+ try{
+ yahooProviderFactory.installAccount(
+ (String)yahooAccount1Properties.get(ProtocolProviderFactory.USER_ID)
+ , yahooAccount1Properties);
+
+ fail("An IllegalStateException must be thrown when trying to "+
+ "install a duplicate account");
+
+ }catch(IllegalStateException exc)
+ {
+ //that's what supposed to happen.
+ }
+
+ //Verify that the provider factory is aware of our installation
+ assertTrue(
+ "The newly installed account was not in the acc man's "
+ +"registered accounts!",
+ yahooProviderFactory.getRegisteredAccounts().size() == 2);
+
+ //Verify protocol providers corresponding to the new account have
+ //been properly registered with the osgi framework.
+
+ osgiFilter =
+ "(&("+ProtocolProviderFactory.PROTOCOL +"="+ProtocolNames.YAHOO+")"
+ +"(" + ProtocolProviderFactory.USER_ID
+ + "=" + (String)yahooAccount1Properties.get(
+ ProtocolProviderFactory.USER_ID)
+ + "))";
+
+ try
+ {
+ serRefs = YahooSlickFixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ osgiFilter);
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ //this really shouldhn't occur as the filter expression is static.
+ fail(osgiFilter + "is not a valid osgi filter");
+ }
+
+ assertTrue("An protocol provider was apparently not installed as "
+ + "requested."
+ , serRefs != null && serRefs.length > 0);
+
+ Object yahooProtocolProvider
+ = YahooSlickFixture.bc.getService(serRefs[0]);
+
+ assertTrue("The installed protocol provider does not implement "
+ + "the protocol provider service."
+ ,yahooProtocolProvider instanceof ProtocolProviderService);
+ }
+
+ /**
+ * Returns all properties necessary for the intialization of the account
+ * with <tt>accountPrefix</tt>.
+ * @param accountPrefix the prefix contained by all property names for the
+ * the account we'd like to initialized
+ * @return a Hashtable that can be used when creating the account in a
+ * protocol provider factory.
+ */
+ private Hashtable getAccountProperties(String accountPrefix)
+ {
+ Hashtable table = new Hashtable();
+
+ String userID = System.getProperty(
+ accountPrefix + ProtocolProviderFactory.USER_ID, null);
+
+ assertNotNull(
+ "The system property named "
+ + accountPrefix + ProtocolProviderFactory.USER_ID
+ +" has to tontain a valid yahoo address that could be used during "
+ +"SIP Communicator's tests."
+ , userID);
+
+ table.put(ProtocolProviderFactory.USER_ID, userID);
+
+ String passwd = System.getProperty(
+ accountPrefix + ProtocolProviderFactory.PASSWORD, null );
+
+ assertNotNull(
+ "The system property named "
+ + accountPrefix + ProtocolProviderFactory.PASSWORD
+ +" has to contain the password corresponding to the account "
+ + "specified in "
+ + accountPrefix + ProtocolProviderFactory.USER_ID
+ , passwd);
+
+ table.put(ProtocolProviderFactory.PASSWORD, passwd);
+
+ String serverAddress = System.getProperty(
+ accountPrefix + ProtocolProviderFactory.SERVER_ADDRESS, null);
+
+ // optional
+ if(serverAddress != null)
+ table.put(ProtocolProviderFactory.SERVER_ADDRESS, serverAddress);
+
+ String serverPort = System.getProperty(
+ accountPrefix + ProtocolProviderFactory.SERVER_PORT, null);
+
+ // optional
+ if(serverPort != null)
+ table.put(ProtocolProviderFactory.SERVER_PORT, serverPort);
+
+ return table;
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountUninstallation.java b/test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountUninstallation.java
new file mode 100644
index 0000000..cd2a15b
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountUninstallation.java
@@ -0,0 +1,270 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import org.osgi.framework.*;
+import junit.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+
+/**
+ * Tests whether accaounts are uninstalled properly. It is important that
+ * tests from this class be called last since they will install the accounts
+ * that have been used to test the implementations. Apart from uninstallation
+ * tests the class also contains tests that remove and reinstall the protocol
+ * provider bundle in order to verify that accounts are persistent.
+ *
+ * @author Emil Ivov
+ */
+public class TestAccountUninstallation
+ extends TestCase
+{
+ private static final Logger logger =
+ Logger.getLogger(TestAccountUninstallation.class);
+
+ private YahooSlickFixture fixture = new YahooSlickFixture();
+
+ /**
+ * Constructs a test instance
+ * @param name The name of the test.
+ */
+ public TestAccountUninstallation(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * JUnit setup method.
+ * @throws Exception in case anything goes wrong.
+ */
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ fixture.setUp();
+ }
+
+ /**
+ * JUnit teardown method.
+ * @throws Exception in case anything goes wrong.
+ */
+ protected void tearDown() throws Exception
+ {
+ fixture.tearDown();
+ super.tearDown();
+ }
+
+ /**
+ * Returns a suite containing tests in this class in the order that we'd
+ * like them executed.
+ * @return a Test suite containing tests in this class in the order that
+ * we'd like them executed.
+ */
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite();
+
+ suite.addTest(
+ new TestAccountUninstallation("testInstallationPersistency"));
+ suite.addTest(
+ new TestAccountUninstallation("testUninstallAccount"));
+
+ return suite;
+ }
+
+ /**
+ * Stops and removes the tested bundle, verifies that it has unregistered
+ * its provider, then reloads and restarts the bundle and verifies that
+ * the protocol provider is reRegistered in the bundle context.
+ *
+ * @throws java.lang.Exception if an exception occurs during testing.
+ */
+ public void testInstallationPersistency() throws Exception
+ {
+ Bundle providerBundle
+ = fixture.findProtocolProviderBundle(fixture.provider1);
+
+ //set the global providerBundle reference that we will be using
+ //in the last series of tests (Account uninstallation persistency)
+ YahooSlickFixture.providerBundle = providerBundle;
+
+ assertNotNull("Couldn't find a bundle for the tested provider"
+ , providerBundle);
+
+ providerBundle.stop();
+
+ assertTrue("Couldn't stop the protocol provider bundle. State was "
+ + providerBundle.getState()
+ , Bundle.ACTIVE != providerBundle.getState()
+ && Bundle.STOPPING != providerBundle.getState());
+
+ providerBundle.uninstall();
+
+ assertEquals("Couldn't stop the protocol provider bundle."
+ , Bundle.UNINSTALLED, providerBundle.getState());
+
+ //verify that the provider is no longer available
+ ServiceReference[] yahooProviderRefs = null;
+ try
+ {
+ yahooProviderRefs = fixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(&"
+ + "(" + ProtocolProviderFactory.PROTOCOL
+ + "=" +ProtocolNames.YAHOO + ")"
+ + "(" + ProtocolProviderFactory.USER_ID
+ + "="+ fixture.userID1 + ")"
+ + ")");
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ fail("We apparently got our filter wrong: " + ex.getMessage());
+ }
+
+ //make sure we didn't see a service
+ assertTrue("A Protocol Provider Service was still regged as an osgi service "
+ +"for yahoo URI:" + fixture.userID1
+ + "After it was explicitly uninstalled"
+ ,yahooProviderRefs == null || yahooProviderRefs.length == 0);
+
+ //verify that the provider factory knows that we have uninstalled the
+ //provider.
+ assertTrue(
+ "The yahoo provider factory kept a reference to the provider we just "
+ +"uninstalled (uri="+fixture.userID1+")",
+ fixture.providerFactory.getRegisteredAccounts().isEmpty()
+ && fixture.providerFactory.getProviderForAccount(
+ fixture.provider1.getAccountID())
+ == null);
+
+ //Now reinstall the bundle
+ providerBundle = fixture.bc.installBundle(providerBundle.getLocation());
+
+ //set the global providerBundle reference that we will be using
+ //in the last series of tests (Account uninstallation persistency)
+ YahooSlickFixture.providerBundle = providerBundle;
+
+ assertEquals("Couldn't re-install protocol provider bundle."
+ , Bundle.INSTALLED, providerBundle.getState());
+
+ providerBundle.start();
+ assertEquals("Couldn't re-start protocol provider bundle."
+ , Bundle.ACTIVE, providerBundle.getState());
+
+ //Make sure that the provider is there again.
+ //verify that the provider is no longer available
+ try
+ {
+ yahooProviderRefs = fixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(&"
+ + "(" + ProtocolProviderFactory.PROTOCOL
+ + "=" +ProtocolNames.YAHOO + ")"
+ + "(" + ProtocolProviderFactory.USER_ID
+ + "="+ fixture.userID1 + ")"
+ + ")");
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ fail("We apparently got our filter wrong " + ex.getMessage());
+ }
+
+ //make sure we didn't see a service
+ assertTrue("A Protocol Provider Service was not restored after being"
+ +"reinstalled. yahoo URI:" + fixture.userID1
+ ,yahooProviderRefs != null && yahooProviderRefs.length > 0);
+
+ ServiceReference[] yahooFactoryRefs = null;
+ try
+ {
+ yahooFactoryRefs = fixture.bc.getServiceReferences(
+ ProtocolProviderFactory.class.getName(),
+ "(" + ProtocolProviderFactory.PROTOCOL
+ + "=" +ProtocolNames.YAHOO + ")");
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ fail("We apparently got our filter wrong " + ex.getMessage());
+ }
+
+ //we're the ones who've reinstalled the factory so it's our
+ //responsibility to update the fixture.
+ fixture.providerFactory
+ = (ProtocolProviderFactory)fixture.bc.getService(yahooFactoryRefs[0]);
+ fixture.provider1
+ = (ProtocolProviderService)fixture.bc.getService(yahooProviderRefs[0]);
+
+
+ //verify that the provider is also restored in the provider factory
+ //itself
+ assertTrue(
+ "The yahoo provider did not restore its own reference to the provider "
+ +"that we just reinstalled (URI="+fixture.userID1+")",
+ !fixture.providerFactory.getRegisteredAccounts().isEmpty()
+ && fixture.providerFactory.getProviderForAccount(
+ fixture.provider1.getAccountID())
+ != null);
+
+ }
+
+ /**
+ * Uinstalls our test account and makes sure it really has been removed.
+ *
+ */
+ public void testUninstallAccount()
+ {
+ assertFalse("No installed accounts found",
+ fixture.providerFactory.getRegisteredAccounts().isEmpty());
+
+ assertNotNull(
+ "Found no provider corresponding to URI " + fixture.userID1
+ ,fixture.providerFactory.getProviderForAccount(
+ fixture.provider1.getAccountID()));
+
+ assertTrue(
+ "Failed to remove a provider corresponding to URI "
+ + fixture.userID1
+ ,fixture.providerFactory.uninstallAccount(
+ fixture.provider1.getAccountID()));
+ assertTrue(
+ "Failed to remove a provider corresponding to URI "
+ + fixture.userID1
+ ,fixture.providerFactory.uninstallAccount(
+ fixture.provider2.getAccountID()));
+
+ //make sure no providers have remained installed.
+ ServiceReference[] yahooProviderRefs = null;
+ try
+ {
+ yahooProviderRefs = fixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(" + ProtocolProviderFactory.PROTOCOL
+ + "=" +ProtocolNames.YAHOO + ")");
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ fail("We apparently got our filter wrong " + ex.getMessage());
+ }
+
+ //make sure we didn't see a service
+ assertTrue("A Protocol Provider Service was still regged as an osgi "
+ + "service for yahoo URI:" + fixture.userID1
+ + "After it was explicitly uninstalled"
+ ,yahooProviderRefs == null || yahooProviderRefs.length == 0);
+
+ //verify that the provider factory knows that we have uninstalled the
+ //provider.
+ assertTrue(
+ "The yahoo provider factory kept a reference to the provider we just "
+ +"uninstalled (uri="+fixture.userID1+")",
+ fixture.providerFactory.getRegisteredAccounts().isEmpty()
+ && fixture.providerFactory.getProviderForAccount(
+ fixture.provider1.getAccountID())
+ == null);
+
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountUninstallationPersistence.java b/test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountUninstallationPersistence.java
new file mode 100644
index 0000000..f621abc
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/TestAccountUninstallationPersistence.java
@@ -0,0 +1,100 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import org.osgi.framework.*;
+import junit.framework.*;
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Contains tests verifying persistence of account uninstallation. In other
+ * words we try to make sure that once uninstalled an account remains
+ * uninstalled.
+ *
+ * @author Emil Ivov
+ */
+public class TestAccountUninstallationPersistence
+ extends TestCase
+{
+ /**
+ * Creates a new test instance wrapper around the test with the specified
+ * name.
+ * @param testName the name of the test that we will be executing.
+ */
+ public TestAccountUninstallationPersistence(String testName)
+ {
+ super(testName);
+ }
+
+ /**
+ * Retrieves a reference to the yahoo bundle, stops it and uninstalls it and
+ * then reinstalls it in order to make sure that accounts are not reloaded
+ * once removed.
+ *
+ * @throws java.lang.Exception if something goes wrong while manipulating
+ * the bundles.
+ */
+ public void testAccountUninstallationPersistence()
+ throws Exception
+ {
+ Bundle providerBundle = YahooSlickFixture.providerBundle;
+
+ providerBundle.stop();
+
+ assertTrue("Couldn't stop the protocol provider bundle. State was "
+ + providerBundle.getState()
+ , Bundle.ACTIVE != providerBundle.getState()
+ && Bundle.STOPPING != providerBundle.getState());
+
+ providerBundle.uninstall();
+
+ assertEquals("Couldn't stop the protocol provider bundle."
+ , Bundle.UNINSTALLED, providerBundle.getState());
+
+ //Now reinstall the bundle and restart the provider
+ providerBundle
+ = YahooSlickFixture.bc.installBundle(providerBundle.getLocation());
+
+ assertEquals("Couldn't re-install protocol provider bundle."
+ , Bundle.INSTALLED, providerBundle.getState());
+
+ providerBundle.start();
+ assertEquals("Couldn't re-start protocol provider bundle."
+ , Bundle.ACTIVE, providerBundle.getState());
+
+
+ //verify that the provider is not reinstalled
+ ServiceReference[] yahooProviderRefs = null;
+ try
+ {
+ yahooProviderRefs = YahooSlickFixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(" + ProtocolProviderFactory.PROTOCOL
+ + "=" +ProtocolNames.YAHOO + ")");
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ fail("We apparently got our filter wrong " + ex.getMessage());
+ }
+
+ //make sure we didn't retrieve a service
+ assertTrue("A yahoo Protocol Provider Service was still regged as an "
+ +"osgi service after it was explicitly uninstalled"
+ ,yahooProviderRefs == null || yahooProviderRefs.length == 0);
+
+ //and a nasty hack at the end - delete the configuration file so that
+ //we get a fresh start on next run.
+ ServiceReference confReference
+ = YahooSlickFixture.bc.getServiceReference(
+ ConfigurationService.class.getName());
+ ConfigurationService configurationService
+ = (ConfigurationService) YahooSlickFixture.bc.getService(confReference);
+
+ configurationService.purgeStoredConfiguration();
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetBasicInstantMessaging.java b/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetBasicInstantMessaging.java
new file mode 100644
index 0000000..917ac4b
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetBasicInstantMessaging.java
@@ -0,0 +1,510 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import java.net.*;
+import java.util.*;
+
+import junit.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.Message;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Performs testing of the basic instant messaging operation set. Tests include
+ * going over basic functionality such as sending a message from the tested
+ * implementation and asserting reception by the tester agent and vice versa.
+ * @author Emil Ivov
+ */
+public class TestOperationSetBasicInstantMessaging
+ extends TestCase
+{
+ private static final Logger logger =
+ Logger.getLogger(TestOperationSetBasicInstantMessaging.class);
+
+ private YahooSlickFixture fixture = new YahooSlickFixture();
+
+ private OperationSetBasicInstantMessaging opSetBasicIM1 = null;
+ private OperationSetBasicInstantMessaging opSetBasicIM2 = null;
+
+ private OperationSetPresence opSetPresence1 = null;
+ private OperationSetPresence opSetPresence2 = null;
+
+ public TestOperationSetBasicInstantMessaging(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Get a reference to the basic IM operation set.
+ * @throws Exception if this is not a good day.
+ */
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ fixture.setUp();
+
+ Map supportedOperationSets1 =
+ fixture.provider1.getSupportedOperationSets();
+
+ if ( supportedOperationSets1 == null
+ || supportedOperationSets1.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ +"this implementation. ");
+
+ //get the operation set presence here.
+ opSetBasicIM1 =
+ (OperationSetBasicInstantMessaging)supportedOperationSets1.get(
+ OperationSetBasicInstantMessaging.class.getName());
+
+ if (opSetBasicIM1 == null)
+ {
+ throw new NullPointerException(
+ "No implementation for basic IM was found");
+ }
+
+ //we also need the presence op set in order to retrieve contacts.
+ opSetPresence1 =
+ (OperationSetPresence)supportedOperationSets1.get(
+ OperationSetPresence.class.getName());
+
+ //if the op set is null show that we're not happy.
+ if (opSetPresence1 == null)
+ {
+ throw new NullPointerException(
+ "An implementation of the service must provide an "
+ + "implementation of at least one of the PresenceOperationSets");
+ }
+
+ Map supportedOperationSets2 =
+ fixture.provider2.getSupportedOperationSets();
+
+ if ( supportedOperationSets2 == null
+ || supportedOperationSets2.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ +"this implementation. ");
+
+ //get the operation set presence here.
+ opSetBasicIM2 =
+ (OperationSetBasicInstantMessaging)supportedOperationSets2.get(
+ OperationSetBasicInstantMessaging.class.getName());
+
+ if (opSetBasicIM2 == null)
+ {
+ throw new NullPointerException(
+ "No implementation for basic IM was found");
+ }
+
+ opSetPresence2 =
+ (OperationSetPresence) supportedOperationSets2.get(
+ OperationSetPresence.class.getName());
+
+ //if the op set is null show that we're not happy.
+ if (opSetPresence2 == null)
+ {
+ throw new NullPointerException(
+ "An implementation of the service must provide an "
+ + "implementation of at least one of the PresenceOperationSets");
+ }
+
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ fixture.tearDown();
+ }
+
+ /**
+ * Creates a test suite containing tests of this class in a specific order.
+ * We'll first execute tests beginning with the "test" prefix and then go to
+ * ordered tests.We first execture tests for receiving messagese, so that
+ * a volatile contact is created for the sender. we'll then be able to
+ * retrieve this volatile contact and send them a message on our turn.
+ * We need to do things this way as the contact corresponding to the tester
+ * agent has been removed in the previous test and we no longer have it
+ * in our contact list.
+ *
+ * @return Test a testsuite containing all tests to execute.
+ */
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite();
+
+ suite.addTest(new TestOperationSetBasicInstantMessaging(
+ "prepareContactList"));
+
+ suite.addTestSuite(TestOperationSetBasicInstantMessaging.class);
+
+ //the following 2 need to be run in the specified order.
+ suite.addTest(new TestOperationSetBasicInstantMessaging(
+ "firstTestReceiveMessage"));
+ suite.addTest(new TestOperationSetBasicInstantMessaging(
+ "thenTestSendMessage"));
+
+ return suite;
+ }
+
+ /**
+ * Create the list to be sure that contacts exchanging messages
+ * exists in each other lists
+ * @throws Exception
+ */
+ public void prepareContactList()
+ throws Exception
+ {
+ fixture.clearProvidersLists();
+
+ Object o = new Object();
+ synchronized(o)
+ {
+ o.wait(2000);
+ }
+
+ try
+ {
+ opSetPresence1.subscribe(fixture.userID2);
+ }
+ catch (OperationFailedException ex)
+ {
+ // the contact already exist its OK
+ }
+
+ try
+ {
+ opSetPresence2.subscribe(fixture.userID1);
+ }
+ catch (OperationFailedException ex1)
+ {
+ // the contact already exist its OK
+ }
+
+ synchronized(o)
+ {
+ o.wait(2000);
+ }
+ }
+
+ /**
+ * Send an instant message from the tested operation set and assert
+ * reception by the tester agent.
+ */
+ public void firstTestReceiveMessage()
+ {
+ String body = "This is an IM coming from the tester agent"
+ + " on " + new Date().toString();
+
+ ImEventCollector evtCollector = new ImEventCollector();
+
+ //add a msg listener and register to the op set and send an instant
+ //msg from the tester agent.
+ opSetBasicIM1.addMessageListener(evtCollector);
+
+ Contact testerAgentContact
+ = opSetPresence2.findContactByID(fixture.userID1);
+
+ logger.debug("Will send message " + body + " to: " + testerAgentContact);
+
+ opSetBasicIM2.sendInstantMessage(testerAgentContact,
+ opSetBasicIM2.createMessage(body));
+
+ evtCollector.waitForEvent(10000);
+
+ opSetBasicIM1.removeMessageListener(evtCollector);
+
+ //assert reception of a message event
+ assertTrue( "No events delivered upon a received message"
+ , evtCollector.collectedEvents.size() > 0);
+
+ //assert event instance of Message Received Evt
+ assertTrue( "Received evt was not an instance of "
+ + MessageReceivedEvent.class.getName()
+ , evtCollector.collectedEvents.get(0)
+ instanceof MessageReceivedEvent);
+
+ //assert source contact == testAgent.uin
+ MessageReceivedEvent evt
+ = (MessageReceivedEvent)evtCollector.collectedEvents.get(0);
+ assertEquals("message sender "
+ , evt.getSourceContact().getAddress()
+ , fixture.userID2);
+
+ //assert messageBody == body
+ assertEquals("message body", body, evt.getSourceMessage().getContent());
+ }
+
+ /**
+ * Send an instant message from the tester agent and assert reception by
+ * the tested implementation
+ */
+ public void thenTestSendMessage()
+ {
+ String body = "This is an IM coming from the tested implementation"
+ + " on " + new Date().toString();
+
+ //create the message
+ net.java.sip.communicator.service.protocol.Message msg
+ = opSetBasicIM1.createMessage(body);
+
+ //register a listener in the op set
+ ImEventCollector imEvtCollector1 = new ImEventCollector();
+ opSetBasicIM1.addMessageListener(imEvtCollector1);
+
+ //register a listener in the tester agent
+ ImEventCollector imEvtCollector2 = new ImEventCollector();
+ opSetBasicIM2.addMessageListener(imEvtCollector2);
+
+ Contact testerAgentContact
+ = opSetPresence1.findContactByID(fixture.userID2);
+
+ opSetBasicIM1.sendInstantMessage(testerAgentContact, msg);
+
+ imEvtCollector1.waitForEvent(10000);
+ imEvtCollector2.waitForEvent(10000);
+
+ opSetBasicIM1.removeMessageListener(imEvtCollector1);
+ opSetBasicIM2.removeMessageListener(imEvtCollector2);
+
+ //verify that the message delivered event was dispatched
+ assertTrue( "No events delivered when sending a message"
+ , imEvtCollector1.collectedEvents.size() > 0);
+
+ assertTrue( "Received evt was not an instance of "
+ + MessageDeliveredEvent.class.getName()
+ , imEvtCollector1.collectedEvents.get(0)
+ instanceof MessageDeliveredEvent);
+
+ MessageDeliveredEvent evt
+ = (MessageDeliveredEvent)imEvtCollector1.collectedEvents.get(0);
+ assertEquals("message destination "
+ , evt.getDestinationContact().getAddress()
+ , fixture.userID2);
+
+ assertSame("source message", msg, evt.getSourceMessage());
+
+
+ //verify that the message has successfully arived at the destination
+ assertTrue( "No messages received by the tester agent"
+ , imEvtCollector2.collectedEvents.size() > 0);
+ String receivedBody =
+ ((MessageReceivedEvent)imEvtCollector2.collectedEvents
+ .get(0)).getSourceMessage().getContent();
+
+ assertEquals("received message body", msg.getContent(), receivedBody);
+ }
+
+ /**
+ * Creates an Message through the simple createMessage() method and inspects
+ * its parameters.
+ */
+ public void testCreateMessage1()
+ {
+ String body = "This is an IM coming from the tested implementation"
+ + " on " + new Date().toString();
+ net.java.sip.communicator.service.protocol.Message msg
+ = opSetBasicIM1.createMessage(body);
+
+ assertEquals("message body", body, msg.getContent());
+ assertTrue("message body bytes"
+ , Arrays.equals(body.getBytes(), msg.getRawData()));
+ assertEquals("message length", body.length(), msg.getSize());
+ assertEquals("message content type"
+ , OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE
+ , msg.getContentType());
+
+ assertEquals("message encoding"
+ , OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING
+ , msg.getEncoding());
+
+ assertNotNull("message uid", msg.getMessageUID());
+
+ //a further test on message uid.
+ net.java.sip.communicator.service.protocol.Message msg2
+ = opSetBasicIM1.createMessage(body);
+ assertFalse("message uid", msg.getMessageUID().equals(
+ msg2.getMessageUID()));
+ }
+
+ /**
+ * Creates an Message through the advance createMessage() method and
+ * inspects its parameters.
+ */
+ public void testCreateMessage2()
+ {
+ String body = "This is an IM coming from the tested implementation"
+ + " on " + new Date().toString();
+ String contentType = "text/html";
+ String encoding = "UTF-16";
+ String subject = "test message";
+ net.java.sip.communicator.service.protocol.Message msg
+ = opSetBasicIM1.createMessage(
+ body.getBytes(), contentType, encoding, subject);
+
+ assertEquals("message body", body, msg.getContent());
+ assertTrue("message body bytes"
+ , Arrays.equals(body.getBytes(), msg.getRawData()));
+ assertEquals("message length", body.length(), msg.getSize());
+ assertEquals("message content type", contentType, msg.getContentType());
+ assertEquals("message encoding", encoding, msg.getEncoding());
+ assertNotNull("message uid", msg.getMessageUID());
+
+ //a further test on message uid.
+ net.java.sip.communicator.service.protocol.Message msg2
+ = opSetBasicIM1.createMessage(body);
+ assertFalse("message uid", msg.getMessageUID().equals(
+ msg2.getMessageUID()));
+ }
+
+ /**
+ * Collects instant messaging events.
+ */
+ private class ImEventCollector implements MessageListener
+ {
+ private List collectedEvents = new LinkedList();
+ /**
+ * Called when a new incoming <tt>Message</tt> has been received.
+ * @param evt the <tt>MessageReceivedEvent</tt> containing the newly
+ * received message, its sender and other details.
+ */
+ public void messageReceived(MessageReceivedEvent evt)
+ {
+ logger.debug("Received a MessageReceivedEvent: " + evt);
+
+ synchronized(this)
+ {
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Called to indicated that delivery of a message sent earlier has failed.
+ * Reason code and phrase are contained by the <tt>MessageFailedEvent</tt>
+ * @param evt the <tt>MessageFailedEvent</tt> containing the ID of the
+ * message whose delivery has failed.
+ */
+ public void messageDeliveryFailed(MessageDeliveryFailedEvent evt)
+ {
+ logger.debug("Received a MessageDeliveryFailedEvent: " + evt);
+
+ synchronized(this)
+ {
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+
+ /**
+ * Called when the underlying implementation has received an indication
+ * that a message, sent earlier has been successfully received by the
+ * destination.
+ * @param evt the MessageDeliveredEvent containing the id of the message
+ * that has caused the event.
+ */
+ public void messageDelivered(MessageDeliveredEvent evt)
+ {
+ logger.debug("Received a MessageDeliveredEvent: " + evt);
+
+ synchronized(this)
+ {
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Blocks until at least one event is received or until waitFor
+ * miliseconds pass (whichever happens first).
+ *
+ * @param waitFor the number of miliseconds that we should be waiting
+ * for an event before simply bailing out.
+ */
+ public void waitForEvent(long waitFor)
+ {
+ synchronized(this)
+ {
+
+ if(collectedEvents.size() > 0)
+ return;
+
+ try{
+ wait(waitFor);
+ }
+ catch (InterruptedException ex)
+ {
+ logger.debug(
+ "Interrupted while waiting for a message evt", ex);
+ }
+ }
+ }
+ }
+
+ /**
+ * A method that would simply send messages to a group of people so that
+ * they would get notified that tests are being run.
+ */
+ public void testSendFunMessages()
+ {
+ String hostname = "";
+
+ try{
+ hostname = java.net.InetAddress.getLocalHost().getHostName() + ": ";
+ }catch (UnknownHostException ex){}
+
+ String message = hostname
+ + "Hello this is the SIP Communicator (version "
+ + System.getProperty("sip-communicator.version")
+ + ") build on: "
+ + new Date().toString()
+ + ". Have a very nice day!";
+
+ String list = System.getProperty("accounts.reporting.YAHOO_REPORT_LIST");
+
+ logger.debug("Will send message " + message + " to: " + list);
+
+ //if no property is specified - return
+ if(list == null || list.trim().length() == 0)
+ return;
+
+ StringTokenizer tokenizer = new StringTokenizer(list, " ");
+
+ while(tokenizer.hasMoreTokens())
+ {
+ String contactID = tokenizer.nextToken();
+ Contact contact
+ = opSetPresence2.findContactByID(contactID);
+
+ if(contact == null)
+ {
+ try
+ {
+ opSetPresence2.subscribe(contactID);
+ Object o = new Object();
+ synchronized (o)
+ {
+ o.wait(2000);
+ }
+ }
+ catch (Exception ex1)
+ {
+ continue;
+ }
+ }
+
+ contact
+ = opSetPresence2.findContactByID(contactID);
+
+ opSetBasicIM2.sendInstantMessage(contact,
+ opSetBasicIM2.createMessage(message));
+ }
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetPersistentPresence.java b/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetPersistentPresence.java
new file mode 100644
index 0000000..ffdc782
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetPersistentPresence.java
@@ -0,0 +1,559 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import java.util.*;
+
+import junit.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * @author Damian Minkov
+ */
+public class TestOperationSetPersistentPresence
+ extends TestCase
+{
+ private static final Logger logger =
+ Logger.getLogger(TestOperationSetPersistentPresence.class);
+
+ private YahooSlickFixture fixture = new YahooSlickFixture();
+ private OperationSetPersistentPresence opSetPersPresence1 = null;
+ private OperationSetPersistentPresence opSetPersPresence2 = null;
+ private static final String testGroupName = "NewGroup";
+ private static final String testGroupName2 = "Renamed";
+
+ public TestOperationSetPersistentPresence(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Creates a test suite containing all tests of this class followed by
+ * test methods that we want executed in a specified order.
+ * @return the Test suite to run
+ */
+ public static Test suite()
+ {
+ TestSuite suite =
+ new TestSuite();
+
+ //the following 2 need to be run in the specified order.
+ //(postTestRemoveGroup() needs the group created from
+ //postTestCreateGroup() )
+ suite.addTest(
+ new TestOperationSetPersistentPresence("postTestCreateGroup"));
+
+ //rename
+ //suite.addTest( new TestOperationSetPersistentPresence(
+ // "postTestRenameGroup"));
+
+ suite.addTest(
+ new TestOperationSetPersistentPresence("postTestRemoveGroup"));
+
+ // create the contact list
+ suite.addTest(
+ new TestOperationSetPersistentPresence("prepareContactList"));
+
+ suite.addTestSuite(TestOperationSetPersistentPresence.class);
+
+ return suite;
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ fixture.setUp();
+
+ Map supportedOperationSets1 =
+ fixture.provider1.getSupportedOperationSets();
+
+ if ( supportedOperationSets1 == null
+ || supportedOperationSets1.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ +"this Yahoo implementation. ");
+
+ //get the operation set presence here.
+ opSetPersPresence1 =
+ (OperationSetPersistentPresence)supportedOperationSets1.get(
+ OperationSetPersistentPresence.class.getName());
+
+ //if still null then the implementation doesn't offer a presence
+ //operation set which is unacceptable for yahoo.
+ if (opSetPersPresence1 == null)
+ throw new NullPointerException(
+ "An implementation of the Yahoo service must provide an "
+ + "implementation of at least the one of the Presence "
+ + "Operation Sets");
+
+ // lets do it once again for the second provider
+ Map supportedOperationSets2 =
+ fixture.provider2.getSupportedOperationSets();
+
+ if (supportedOperationSets2 == null
+ || supportedOperationSets2.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ + "this Yahoo implementation. ");
+
+ //get the operation set presence here.
+ opSetPersPresence2 =
+ (OperationSetPersistentPresence) supportedOperationSets2.get(
+ OperationSetPersistentPresence.class.getName());
+
+ //if still null then the implementation doesn't offer a presence
+ //operation set which is unacceptable for yahoo.
+ if (opSetPersPresence2 == null)
+ throw new NullPointerException(
+ "An implementation of the yahoo service must provide an "
+ + "implementation of at least the one of the Presence "
+ + "Operation Sets");
+ }
+
+ protected void tearDown() throws Exception
+ {
+ fixture.tearDown();
+ super.tearDown();
+ }
+
+ /**
+ * Retrieves a server stored contact list and checks whether it contains
+ * all contacts that have been added there during the initialization
+ * phase by the testerAgent.
+ */
+ public void testRetrievingServerStoredContactList()
+ {
+ ContactGroup rootGroup
+ = opSetPersPresence1.getServerStoredContactListRoot();
+
+ logger.debug("=========== Server Stored Contact List =================");
+
+ logger.debug("rootGroup="+rootGroup.getGroupName()
+ +" rootGroup.childContacts="+rootGroup.countContacts()
+ + "rootGroup.childGroups="+rootGroup.countSubgroups()
+ + "Printing rootGroupContents=\n"+rootGroup.toString());
+
+ Hashtable expectedContactList = fixture.preInstalledBuddyList;
+
+ logger.debug("============== Expected Contact List ===================");
+ logger.debug(expectedContactList);
+
+ //Go through the contact list retrieved by the persistence presence set
+ //and remove the name of every contact and group that we find there from
+ //the expected contct list hashtable.
+ Iterator groups = rootGroup.subgroups();
+ while (groups.hasNext() )
+ {
+ ContactGroup group = (ContactGroup)groups.next();
+
+ List expectedContactsInGroup
+ = (List)expectedContactList.get(group.getGroupName());
+
+ // When sending the offline message
+ // the sever creates a group NotInContactList,
+ // beacuse the buddy we are sending message to is not in
+ // the contactlist. So this group must be ignored
+ // Also we must ignore the group created by default
+ // from the yahoo lib
+ if(!group.getGroupName().equals("NotInContactList") &&
+ !group.getGroupName().equals("Default group"))
+ {
+ assertNotNull("Group " + group.getGroupName() +
+ " was returned by "
+ +
+ "the server but was not in the expected contact list."
+ , expectedContactsInGroup);
+
+ Iterator contactsIter = group.contacts();
+ while(contactsIter.hasNext())
+ {
+ String contactID = ((Contact)contactsIter.next()).
+ getAddress();
+ expectedContactsInGroup.remove(contactID);
+ }
+
+ //If we've removed all the sub contacts, remove the group too.
+ if(expectedContactsInGroup.size() == 0)
+ expectedContactList.remove(group.getGroupName());
+ }
+ }
+
+ //whatever we now have in the expected contact list snapshot are groups,
+ //that have been added by the testerAgent but that were not retrieved
+ //by the persistent presence operation set.
+ assertTrue("The following contacts were on the server sidec contact "
+ +"list, but were not returned by the pers. pres. op. set"
+ + expectedContactList.toString()
+ , expectedContactList.isEmpty());
+ }
+
+ /**
+ * Creates a group in the server stored contact list, makes sure that the
+ * corresponding event has been generated and verifies that the group is
+ * in the list.
+ *
+ * @throws java.lang.Exception
+ */
+ public void postTestCreateGroup()
+ throws Exception
+ {
+ // first clear the list
+ fixture.clearProvidersLists();
+
+ waitFor(5000);
+
+ logger.trace("testing creation of server stored groups");
+ //first add a listener
+ GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
+ opSetPersPresence1
+ .addServerStoredGroupChangeListener(groupChangeCollector);
+
+ //create the group
+ opSetPersPresence1.createServerStoredContactGroup(
+ opSetPersPresence1.getServerStoredContactListRoot(), testGroupName);
+
+ groupChangeCollector.waitForEvent(10000);
+
+ opSetPersPresence1
+ .removeServerStoredGroupChangeListener(groupChangeCollector);
+
+ // check whether we got group created event
+ assertEquals("Collected Group Change events: ",
+ 1, groupChangeCollector.collectedEvents.size());
+
+ assertEquals("Group name.", testGroupName,
+ ((ServerStoredGroupEvent)groupChangeCollector.collectedEvents
+ .get(0)).getSourceGroup().getGroupName());
+
+ // check whether the group is retrievable
+ ContactGroup group = opSetPersPresence1.getServerStoredContactListRoot()
+ .getGroup(testGroupName);
+
+ assertNotNull("A newly created group was not in the contact list.",
+ group);
+
+ assertEquals("New group name", testGroupName, group.getGroupName());
+
+ // when opearting with groups . the group must have entries
+ // so changes to take effect. Otherwise group will be lost after loggingout
+ try
+ {
+ opSetPersPresence1.subscribe(group, fixture.userID2);
+
+ waitFor(1500);
+ }
+ catch (Exception ex)
+ {
+ fail("error adding entry to group : " +
+ group.getGroupName() + " " +
+ ex.getMessage());
+ }
+ }
+
+
+ /**
+ * Removes the group created in the server stored contact list by the create
+ * group test, makes sure that the corresponding event has been generated
+ * and verifies that the group is not in the list any more.
+ */
+ public void postTestRemoveGroup()
+ {
+ logger.trace("testing removal of server stored groups");
+
+ //first add a listener
+ GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
+ opSetPersPresence1
+ .addServerStoredGroupChangeListener(groupChangeCollector);
+
+ //remove the group
+ opSetPersPresence1.removeServerStoredContactGroup(
+ opSetPersPresence1.getServerStoredContactListRoot()
+ .getGroup(testGroupName));
+
+ groupChangeCollector.waitForEvent(10000);
+
+ opSetPersPresence1
+ .removeServerStoredGroupChangeListener(groupChangeCollector);
+
+ // check whether we got group created event
+ assertEquals("Collected Group Change event",
+ 1, groupChangeCollector.collectedEvents.size());
+
+ assertEquals("Group name.", testGroupName,
+ ((ServerStoredGroupEvent)groupChangeCollector.collectedEvents
+ .get(0)).getSourceGroup().getGroupName());
+
+ // check whether the group is still on the contact list
+ ContactGroup group = opSetPersPresence1.getServerStoredContactListRoot()
+ .getGroup(testGroupName);
+
+ assertNull("A freshly removed group was still on the contact list. - " + group,
+ group);
+ }
+
+ /**
+ * Renames our test group and checks whether corresponding events are
+ * triggered. Verifies whether the group has really changed its name and
+ * whether it is findable by its new name. Also makes sure that it does
+ * not exist under its previous name any more.
+ */
+ public void postTestRenameGroup()
+ {
+ logger.trace("Testing renaming groups.");
+
+ ContactGroup group = opSetPersPresence1.getServerStoredContactListRoot()
+ .getGroup(testGroupName);
+
+ //first add a listener
+ GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
+ opSetPersPresence1
+ .addServerStoredGroupChangeListener(groupChangeCollector);
+
+ //change the name and wait for a confirmation event
+ opSetPersPresence1.renameServerStoredContactGroup(group, testGroupName2);
+
+ groupChangeCollector.waitForEvent(10000);
+
+ opSetPersPresence1
+ .removeServerStoredGroupChangeListener(groupChangeCollector);
+
+ //examine the event
+ assertEquals("Collected Group Change event",
+ 1, groupChangeCollector.collectedEvents.size());
+
+ assertEquals("Group name.", testGroupName2,
+ ((ServerStoredGroupEvent)groupChangeCollector.collectedEvents
+ .get(0)).getSourceGroup().getGroupName());
+
+ // check whether the group is still on the contact list
+ ContactGroup oldGroup = opSetPersPresence1.getServerStoredContactListRoot()
+ .getGroup(testGroupName);
+
+ assertNull("A group was still findable by its old name after renaming.",
+ oldGroup);
+
+ //make sure that we could find the group by its new name.
+ ContactGroup newGroup = opSetPersPresence1.getServerStoredContactListRoot()
+ .getGroup(testGroupName2);
+
+ assertNotNull("Could not find a renamed group by its new name.",
+ newGroup);
+ }
+
+ /**
+ * Create the contact list. Later will be test to be sure that creating is ok
+ * @throws Exception
+ */
+ public void prepareContactList()
+ throws Exception
+ {
+ fixture.clearProvidersLists();
+
+ waitFor(3000);
+
+ String contactList = System.getProperty(
+ YahooProtocolProviderServiceLick.CONTACT_LIST_PROPERTY_NAME, null);
+
+ logger.debug("The "
+ + YahooProtocolProviderServiceLick.CONTACT_LIST_PROPERTY_NAME
+ + " property is set to=" + contactList);
+
+ if( contactList == null
+ || contactList.trim().length() < 6)//at least 4 for a UIN, 1 for the
+ // dot and 1 for the grp name
+ throw new IllegalArgumentException(
+ "The " +
+ YahooProtocolProviderServiceLick.CONTACT_LIST_PROPERTY_NAME +
+ " property did not contain a contact list.");
+ StringTokenizer tokenizer = new StringTokenizer(contactList, " \n\t");
+
+ logger.debug("tokens contained by the CL tokenized="
+ +tokenizer.countTokens());
+
+ Hashtable contactListToCreate = new Hashtable();
+
+ //go over all group.uin tokens
+ while (tokenizer.hasMoreTokens())
+ {
+ String groupUinToken = tokenizer.nextToken();
+ int dotIndex = groupUinToken.indexOf(".");
+
+ if ( dotIndex == -1 )
+ {
+ throw new IllegalArgumentException(groupUinToken
+ + " is not a valid Group.UIN token");
+ }
+
+ String groupName = groupUinToken.substring(0, dotIndex);
+ String uin = groupUinToken.substring(dotIndex + 1);
+
+ if( groupName.trim().length() < 1
+ || uin.trim().length() < 4 )
+ {
+ throw new IllegalArgumentException(
+ groupName + " or " + uin +
+ " are not a valid group name or yahoo UIN.");
+ }
+
+ //check if we've already seen this group and if not - add it
+ List uinInThisGroup = (List)contactListToCreate.get(groupName);
+ if (uinInThisGroup == null)
+ {
+ uinInThisGroup = new ArrayList();
+ contactListToCreate.put(groupName, uinInThisGroup);
+ }
+
+ uinInThisGroup.add(uin);
+ }
+
+ // now init the list
+ Enumeration newGroupsEnum = contactListToCreate.keys();
+
+ GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
+ opSetPersPresence1.addServerStoredGroupChangeListener(groupChangeCollector);
+
+ //go over all groups in the contactsToAdd table
+ while (newGroupsEnum.hasMoreElements())
+ {
+ String groupName = (String) newGroupsEnum.nextElement();
+ logger.debug("Will add group " + groupName);
+
+ opSetPersPresence1.createServerStoredContactGroup(
+ opSetPersPresence1.getServerStoredContactListRoot(), groupName);
+
+ groupChangeCollector.waitForEvent(3000);
+
+ ContactGroup newlyCreatedGroup =
+ opSetPersPresence1.getServerStoredContactListRoot().getGroup(groupName);
+
+ Iterator contactsToAddToThisGroup
+ = ( (List) contactListToCreate.get(groupName)).iterator();
+ while (contactsToAddToThisGroup.hasNext())
+ {
+ String id = (String) contactsToAddToThisGroup.next();
+
+ logger.debug("Will add buddy " + id);
+ opSetPersPresence1.subscribe(newlyCreatedGroup, id);
+ }
+ }
+
+ waitFor(2000);
+
+ //store the created contact list for later reference
+ YahooSlickFixture.preInstalledBuddyList = contactListToCreate;
+ }
+
+ private void waitFor(long time)
+ throws Exception
+ {
+ Object o = new Object();
+ synchronized(o)
+ {
+ o.wait(time);
+ }
+ }
+
+ /**
+ * The class would listen for and store received events delivered to
+ * <tt>ServerStoredGroupListener</tt>s.
+ */
+ private class GroupChangeCollector implements ServerStoredGroupListener
+ {
+ public ArrayList collectedEvents = new ArrayList();
+
+ /**
+ * Blocks until at least one event is received or until waitFor
+ * miliseconds pass (whicever happens first).
+ *
+ * @param waitFor the number of miliseconds that we should be waiting
+ * for an event before simply bailing out.
+ */
+ public void waitForEvent(long waitFor)
+ {
+ synchronized(this)
+ {
+ if(collectedEvents.size() > 0)
+ return;
+
+ try{
+ wait(waitFor);
+ }
+ catch (InterruptedException ex)
+ {
+ logger.debug(
+ "Interrupted while waiting for a subscription evt", ex);
+ }
+ }
+ }
+
+ /**
+ * Called whnever an indication is received that a new server stored
+ * group is created.
+ * @param evt a ServerStoredGroupChangeEvent containing a reference to
+ * the newly created group.
+ */
+ public void groupCreated(ServerStoredGroupEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Called when an indication is received that the name of a server stored
+ * contact group has changed.
+ * @param evt a ServerStoredGroupChangeEvent containing the details of the
+ * name change.
+ */
+ public void groupNameChanged(ServerStoredGroupEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Called whnever an indication is received that an existing server stored
+ * group has been removed.
+ * @param evt a ServerStoredGroupChangeEvent containing a reference to the
+ * newly created group.
+ */
+ public void groupRemoved(ServerStoredGroupEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Called whnever an indication is received that an existing server
+ * stored group has been resolved.
+ * @param evt a ServerStoredGroupChangeEvent containing a reference to
+ * the resolved group.
+ */
+ public void groupResolved(ServerStoredGroupEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetPresence.java b/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetPresence.java
new file mode 100644
index 0000000..caa2df4
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetPresence.java
@@ -0,0 +1,989 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import java.beans.*;
+import java.util.*;
+
+import junit.framework.*;
+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.*;
+
+/**
+ * Tests yahoo implementations of a Presence Operation Set. Tests in this class
+ * verify functionality such as: Changing local (our own) status and
+ * corresponding event dispatching; Querying status of contacts, Subscribing
+ * for presence notifications upong status changes of specific contacts.
+ * <p>
+ * Using a custom suite() method, we make sure that apart from standard test
+ * methods (those with a <tt>test</tt> prefix) we also execute those that
+ * we want run in a specific order like for example - postTestSubscribe() and
+ * postTestUnsubscribe().
+ * <p>
+ * @author Damian Minkov
+ */
+public class TestOperationSetPresence
+ extends TestCase
+{
+ private static final Logger logger =
+ Logger.getLogger(TestOperationSetPresence.class);
+
+ private YahooSlickFixture fixture = new YahooSlickFixture();
+ private OperationSetPresence operationSetPresence1 = null;
+ private OperationSetPresence operationSetPresence2 = null;
+ private String statusMessageRoot = new String("Our status is now: ");
+
+ private AuthHandler authHandler1 = null;
+ private AuthHandler authHandler2 = null;
+
+ public TestOperationSetPresence(String name)
+ {
+ super(name);
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ fixture.setUp();
+
+ Map supportedOperationSets1 =
+ fixture.provider1.getSupportedOperationSets();
+
+ if ( supportedOperationSets1 == null
+ || supportedOperationSets1.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ +"this implementation. ");
+
+ //get the operation set presence here.
+ operationSetPresence1 =
+ (OperationSetPresence)supportedOperationSets1.get(
+ OperationSetPresence.class.getName());
+
+ //if the op set is null then the implementation doesn't offer a presence
+ //operation set which is unacceptable for yahoo.
+ if (operationSetPresence1 == null)
+ {
+ throw new NullPointerException(
+ "An implementation of the yahoo service must provide an "
+ + "implementation of at least the one of the Presence "
+ + "Operation Sets");
+ }
+
+ // do it once again for the second provider
+ Map supportedOperationSets2 =
+ fixture.provider2.getSupportedOperationSets();
+
+ if ( supportedOperationSets2 == null
+ || supportedOperationSets2.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ +"this yahoo implementation. ");
+
+ //get the operation set presence here.
+ operationSetPresence2 =
+ (OperationSetPresence)supportedOperationSets2.get(
+ OperationSetPresence.class.getName());
+
+ //if the op set is null then the implementation doesn't offer a presence
+ //operation set which is unacceptable for yahoo.
+ if (operationSetPresence2 == null)
+ {
+ throw new NullPointerException(
+ "An implementation of the yahoo service must provide an "
+ + "implementation of at least the one of the Presence "
+ + "Operation Sets");
+ }
+
+ if(authHandler1 == null)
+ {
+ authHandler1 = new AuthHandler(operationSetPresence1);
+ operationSetPresence1.setAuthorizationHandler(authHandler1);
+ }
+
+ if(authHandler2 == null)
+ {
+ authHandler2 = new AuthHandler(operationSetPresence2);
+ operationSetPresence2.setAuthorizationHandler(authHandler2);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ fixture.tearDown();
+ }
+
+ /**
+ * Creates a test suite containing all tests of this class followed by
+ * test methods that we want executed in a specified order.
+ * @return Test
+ */
+ public static Test suite()
+ {
+ //return an (almost) empty suite if we're running in offline mode.
+ if(YahooSlickFixture.onlineTestingDisabled)
+ {
+ TestSuite suite = new TestSuite();
+ //the only test around here that we could run without net
+ //connectivity
+ suite.addTest(
+ new TestOperationSetPresence(
+ "testSupportedStatusSetForCompleteness"));
+ return suite;
+ }
+
+ TestSuite suite = new TestSuite();
+
+ // clear the lists before subscribing users
+ suite.addTest(new TestOperationSetPresence("clearLists"));
+
+ // first postTestSubscribe. to be sure that contacts are in the
+ // list so we can further continue and test presences each other
+ suite.addTest(new TestOperationSetPresence("postTestSubscribe"));
+
+ // add other tests
+ suite.addTestSuite(TestOperationSetPresence.class);
+
+ // now test unsubscribe
+ suite.addTest(new TestOperationSetPresence("postTestUnsubscribe"));
+
+ return suite;
+ }
+
+ /**
+ * Verifies that all necessary yahoo test states are supported by the
+ * implementation.
+ */
+ public void testSupportedStatusSetForCompleteness()
+ {
+ //first create a local list containing the presence status instances
+ //supported by the underlying implementation.
+ Iterator supportedStatusSetIter =
+ operationSetPresence1.getSupportedStatusSet();
+
+ List supportedStatusSet = new LinkedList();
+ while (supportedStatusSetIter.hasNext()){
+ supportedStatusSet.add(supportedStatusSetIter.next());
+ }
+
+ //create a copy of the MUST status set and remove any matching status
+ //that is also present in the supported set.
+ List requiredStatusSetCopy = (List)YahooStatusEnum.yahooStatusSet.clone();
+
+ requiredStatusSetCopy.removeAll(supportedStatusSet);
+
+ //if we have anything left then the implementation is wrong.
+ int unsupported = requiredStatusSetCopy.size();
+ assertTrue( "There are " + unsupported + " statuses as follows:"
+ + requiredStatusSetCopy,
+ unsupported == 0);
+ }
+
+ /**
+ * Verify that changing state to STEPPED_OUT works as supposed to and that it
+ * generates the corresponding event.
+ * @throws Exception in case a failure occurs while the operation set
+ * is switching to the new state.
+ */
+ public void testChangingStateToSteppedOut() throws Exception
+ {
+ subtestStateTransition(YahooStatusEnum.STEPPED_OUT);
+ }
+
+ /**
+ * Verify that changing state to NOT_IN_OFFICE works as supposed to and that it
+ * generates the corresponding event.
+ * @throws Exception in case a failure occurs while the operation set
+ * is switching to the new state.
+ */
+ public void testChangingStateToNotInOffice() throws Exception
+ {
+ subtestStateTransition(YahooStatusEnum.NOT_IN_OFFICE);
+ }
+
+ /**
+ * Verify that changing state to BUSY works as supposed to and that it
+ * generates the corresponding event.
+ * @throws Exception in case a failure occurs while the operation set
+ * is switching to the new state.
+ */
+ public void testChangingStateToBusy() throws Exception
+ {
+ subtestStateTransition(YahooStatusEnum.BUSY);
+ }
+
+ /**
+ * Verify that changing state to FREE_FOR_CHAT works as supposed to and that it
+ * generates the corresponding event.
+ * @throws Exception in case a failure occurs while the operation set
+ * is switching to the new state.
+ */
+ public void testChangingStateToIdle() throws Exception
+ {
+ subtestStateTransition(YahooStatusEnum.IDLE);
+ }
+
+ /**
+ * Verify that changing state to ONLINE works as supposed to and that it
+ * generates the corresponding event.
+ * @throws Exception in case a failure occurs while the operation set
+ * is switching to the new state.
+ */
+ public void testChangingStateToOnline() throws Exception
+ {
+ subtestStateTransition(YahooStatusEnum.AVAILABLE);
+ }
+
+ /**
+ * Verify that changing state to OUT_TO_LUNCH works as supposed to and that it
+ * generates the corresponding event.
+ * @throws Exception in case a failure occurs while the operation set
+ * is switching to the new state.
+ */
+ public void testChangingStateToOutToLunch() throws Exception
+ {
+ subtestStateTransition(YahooStatusEnum.OUT_TO_LUNCH);
+ }
+
+ /**
+ * Verify that changing state to ON_THE_PHONE works as supposed to and that it
+ * generates the corresponding event.
+ * @throws Exception in case a failure occurs while the operation set
+ * is switching to the new state.
+ */
+ public void testChangingStateToOnThePhone() throws Exception
+ {
+ subtestStateTransition(YahooStatusEnum.ON_THE_PHONE);
+ }
+
+ /**
+ * Used by methods testing state transiotions
+ *
+ * @param newStatus the YahooStatusEnum field corresponding to the status
+ * that we'd like the opeation set to enter.
+ *
+ * @throws Exception in case changing the state causes an exception
+ */
+ public void subtestStateTransition( YahooStatusEnum newStatus)
+ throws Exception
+ {
+ logger.trace(" --=== beginning state transition test ===--");
+
+ PresenceStatus oldStatus = operationSetPresence1.getPresenceStatus();
+
+ logger.debug( "old status is=" + oldStatus.getStatusName()
+ + " new status=" + newStatus.getStatusName());
+
+ //First register a listener to make sure that all corresponding
+ //events have been generated.
+ PresenceStatusEventCollector statusEventCollector
+ = new PresenceStatusEventCollector();
+ operationSetPresence1.addProviderPresenceStatusListener(
+ statusEventCollector);
+
+ //change the status
+ operationSetPresence1.publishPresenceStatus(newStatus, null);
+ pauseAfterStateChanges();
+
+ //test event notification.
+ statusEventCollector.waitForPresEvent(10000);
+
+ operationSetPresence1.removeProviderPresenceStatusListener(
+ statusEventCollector);
+
+ assertEquals("Events dispatched during an event transition.",
+ 1, statusEventCollector.collectedPresEvents.size());
+ assertEquals("A status changed event contained wrong old status.",
+ oldStatus,
+ ((ProviderPresenceStatusChangeEvent)
+ statusEventCollector.collectedPresEvents.get(0))
+ .getOldStatus());
+ assertEquals("A status changed event contained wrong new status.",
+ newStatus,
+ ((ProviderPresenceStatusChangeEvent)
+ statusEventCollector.collectedPresEvents.get(0))
+ .getNewStatus());
+
+ // verify that the operation set itself is aware of the status change
+ assertEquals("opSet.getPresenceStatus() did not return properly.",
+ newStatus,
+ operationSetPresence1.getPresenceStatus());
+
+ YahooStatusEnum actualStatus = (YahooStatusEnum)
+ operationSetPresence2.queryContactStatus(fixture.userID1);
+
+ assertEquals("The underlying implementation did not switch to the "
+ +"requested presence status.",
+ newStatus,
+ actualStatus);
+
+ logger.trace(" --=== finished test ===--");
+ }
+
+ /**
+ * Give time changes to take effect
+ */
+ private void pauseAfterStateChanges()
+ {
+ try
+ {
+ Thread.currentThread().sleep(1500);
+ }
+ catch (InterruptedException ex)
+ {
+ logger.debug("Pausing between state changes was interrupted", ex);
+ }
+ }
+ /**
+ * Verifies that querying status works fine. The tester agent would
+ * change status and the operation set would have to return the right status
+ * after every change.
+ *
+ * @throws java.lang.Exception if one of the transitions fails
+ */
+ public void testQueryContactStatus()
+ throws Exception
+ {
+ // --- NA ---
+ logger.debug("Will Query an BRB contact.");
+ subtestQueryContactStatus(YahooStatusEnum.BE_RIGHT_BACK,
+ YahooStatusEnum.BE_RIGHT_BACK);
+
+ // --- DND ---
+ logger.debug("Will Query a Busy contact.");
+ subtestQueryContactStatus(YahooStatusEnum.BUSY,
+ YahooStatusEnum.BUSY);
+
+ // --- FFC ---
+ logger.debug("Will Query a Idle contact.");
+ subtestQueryContactStatus(YahooStatusEnum.IDLE,
+ YahooStatusEnum.IDLE);
+
+ // --- INVISIBLE ---
+ logger.debug("Will Query an Invisible contact.");
+ subtestQueryContactStatus(YahooStatusEnum.INVISIBLE,
+ YahooStatusEnum.OFFLINE);
+
+ // --- Online ---
+ logger.debug("Will Query an Online contact.");
+ subtestQueryContactStatus(YahooStatusEnum.AVAILABLE,
+ YahooStatusEnum.AVAILABLE);
+ }
+
+ /**
+ * Used by functions testing the queryContactStatus method of the
+ * presence operation set.
+ * @param status the status as specified, that
+ * the tester agent should switch to.
+ * @param expectedReturn the PresenceStatus that the presence operation
+ * set should see the tester agent in once it has switched to taStatusLong.
+ *
+ * @throws java.lang.Exception if querying the status causes some exception.
+ */
+ public void subtestQueryContactStatus(PresenceStatus status,
+ PresenceStatus expectedReturn)
+ throws Exception
+ {
+ operationSetPresence2.publishPresenceStatus(status, "status message");
+
+ pauseAfterStateChanges();
+
+ PresenceStatus actualReturn
+ = operationSetPresence1.queryContactStatus(fixture.userID2);
+ assertEquals("Querying a "
+ + expectedReturn.getStatusName()
+ + " state did not return as expected"
+ , expectedReturn, actualReturn);
+ }
+
+ /**
+ * The method would add a subscription for a contact, wait for a
+ * subscription event confirming the subscription, then change the status
+ * of the newly added contact (which is actually the testerAgent) and
+ * make sure that the corresponding notification events have been generated.
+ *
+ * @throws java.lang.Exception if an exception occurs during testing.
+ */
+ public void postTestSubscribe()
+ throws Exception
+ {
+ logger.debug("Testing Subscription and Subscription Event Dispatch.");
+
+ SubscriptionEventCollector subEvtCollector
+ = new SubscriptionEventCollector();
+ operationSetPresence1.addSubsciptionListener(subEvtCollector);
+
+
+ synchronized (subEvtCollector){
+ operationSetPresence1.subscribe(fixture.userID2);
+ //we may already have the event, but it won't hurt to check.
+ subEvtCollector.waitForEvent(10000);
+ operationSetPresence1.removeSubscriptionListener(subEvtCollector);
+ }
+
+ assertEquals("Subscription event dispatching failed."
+ , 1, subEvtCollector.collectedEvents.size());
+ SubscriptionEvent subEvt =
+ (SubscriptionEvent)subEvtCollector.collectedEvents.get(0);
+
+ assertEquals("SubscriptionEvent Source:",
+ fixture.userID2,
+ ((Contact)subEvt.getSource()).getAddress());
+ assertEquals("SubscriptionEvent Source Contact:",
+ fixture.userID2,
+ subEvt.getSourceContact().getAddress());
+ assertSame("SubscriptionEvent Source Provider:",
+ fixture.provider1,
+ subEvt.getSourceProvider());
+
+ subEvtCollector.collectedEvents.clear();
+
+ // make the user agent tester change its states and make sure we are
+ // notified
+ logger.debug("Testing presence notifications.");
+ YahooStatusEnum oldStatus
+ = (YahooStatusEnum)operationSetPresence2.getPresenceStatus();
+
+
+ YahooStatusEnum newStatus = YahooStatusEnum.IDLE;
+
+ //in case we are by any chance already in a FREE_FOR_CHAT status, we'll
+ //be changing to something else
+ if(oldStatus.equals(newStatus)){
+ newStatus = YahooStatusEnum.BUSY;
+ }
+
+ //now do the actual status notification testing
+ ContactPresenceEventCollector contactPresEvtCollector
+ = new ContactPresenceEventCollector(
+ fixture.userID2, newStatus);
+ operationSetPresence1.addContactPresenceStatusListener(
+ contactPresEvtCollector);
+
+ synchronized (contactPresEvtCollector){
+ operationSetPresence2.publishPresenceStatus(newStatus, "new status");
+ //we may already have the event, but it won't hurt to check.
+ contactPresEvtCollector.waitForEvent(10000);
+ operationSetPresence1
+ .removeContactPresenceStatusListener(contactPresEvtCollector);
+ }
+
+ assertEquals("Presence Notif. event dispatching failed."
+ , 1, contactPresEvtCollector.collectedEvents.size());
+ ContactPresenceStatusChangeEvent presEvt =
+ (ContactPresenceStatusChangeEvent)
+ contactPresEvtCollector.collectedEvents.get(0);
+
+ assertEquals("Presence Notif. event Source:",
+ fixture.userID2,
+ ((Contact)presEvt.getSource()).getAddress());
+ assertEquals("Presence Notif. event Source Contact:",
+ fixture.userID2,
+ presEvt.getSourceContact().getAddress());
+ assertSame("Presence Notif. event Source Provider:",
+ fixture.provider1,
+ presEvt.getSourceProvider());
+
+ PresenceStatus reportedNewStatus = presEvt.getNewStatus();
+ PresenceStatus reportedOldStatus = presEvt.getOldStatus();
+
+ assertEquals( "Reported new PresenceStatus: ",
+ newStatus, reportedNewStatus );
+
+ //don't require equality between the reported old PresenceStatus and
+ //the actual presence status of the tester agent because a first
+ //notification is not supposed to have the old status as it really was.
+ assertNotNull( "Reported old PresenceStatus: ", reportedOldStatus );
+
+ try
+ {
+ // add the the user to the reverse side needed for status tests
+ subEvtCollector.collectedEvents.clear();
+ operationSetPresence2.addSubsciptionListener(subEvtCollector);
+
+ synchronized (subEvtCollector)
+ {
+ operationSetPresence2.subscribe(fixture.userID1);
+ //we may already have the event, but it won't hurt to check.
+ subEvtCollector.waitForEvent(10000);
+ operationSetPresence2.removeSubscriptionListener(
+ subEvtCollector);
+ }
+ }
+ catch (OperationFailedException ex)
+ {
+ // happens if the user is already subscribed
+ }
+ }
+
+ /**
+ * We unsubscribe from presence notification deliveries concerning
+ * testerAgent's presence status and verify that we receive the
+ * subscription removed event. We then make the tester agent change status
+ * and make sure that no notifications are delivered.
+ *
+ * @throws java.lang.Exception in case unsubscribing fails.
+ */
+ public void postTestUnsubscribe()
+ throws Exception
+ {
+ logger.debug("Testing Unsubscribe and unsubscription event dispatch.");
+
+ // First create a subscription and verify that it really gets created.
+ SubscriptionEventCollector subEvtCollector
+ = new SubscriptionEventCollector();
+ operationSetPresence1.addSubsciptionListener(subEvtCollector);
+
+ Contact yahooTesterAgentContact = operationSetPresence1
+ .findContactByID(fixture.userID2);
+
+ assertNotNull(
+ "Failed to find an existing subscription for the tester agent"
+ , yahooTesterAgentContact);
+
+ synchronized(subEvtCollector){
+ operationSetPresence1.unsubscribe(yahooTesterAgentContact);
+ subEvtCollector.waitForEvent(10000);
+ //don't want any more events
+ operationSetPresence1.removeSubscriptionListener(subEvtCollector);
+ }
+
+ assertEquals("Subscription event dispatching failed."
+ , 1, subEvtCollector.collectedEvents.size());
+ SubscriptionEvent subEvt =
+ (SubscriptionEvent)subEvtCollector.collectedEvents.get(0);
+
+ assertEquals("SubscriptionEvent Source:",
+ yahooTesterAgentContact, subEvt.getSource());
+
+ assertEquals("SubscriptionEvent Source Contact:",
+ yahooTesterAgentContact, subEvt.getSourceContact());
+
+ assertSame("SubscriptionEvent Source Provider:",
+ fixture.provider1,
+ subEvt.getSourceProvider());
+
+ subEvtCollector.collectedEvents.clear();
+
+ // make the user agent tester change its states and make sure we don't
+ // get notifications as we're now unsubscribed.
+ logger.debug("Testing (lack of) presence notifications.");
+ YahooStatusEnum oldStatus
+ = (YahooStatusEnum)operationSetPresence2.getPresenceStatus();
+ YahooStatusEnum newStatus = YahooStatusEnum.IDLE;
+
+ //in case we are by any chance already in a FREE_FOR_CHAT status, we'll
+ //be changing to something else
+ if(oldStatus.equals(newStatus)){
+ newStatus = YahooStatusEnum.BUSY;
+ }
+
+ //now do the actual status notification testing
+ ContactPresenceEventCollector contactPresEvtCollector
+ = new ContactPresenceEventCollector(fixture.userID2, null);
+ operationSetPresence1.addContactPresenceStatusListener(
+ contactPresEvtCollector);
+
+ synchronized (contactPresEvtCollector){
+ operationSetPresence2.publishPresenceStatus(newStatus, "new status");
+
+ //we may already have the event, but it won't hurt to check.
+ contactPresEvtCollector.waitForEvent(10000);
+ operationSetPresence1
+ .removeContactPresenceStatusListener(contactPresEvtCollector);
+ }
+
+ assertEquals("Presence Notifications were received after unsubscibing."
+ , 0, contactPresEvtCollector.collectedEvents.size());
+ }
+
+ public void clearLists()
+ throws Exception
+ {
+ logger.debug("Clear the two lists before tests");
+
+ // wait for a moment
+ // give time the impl to get the lists
+ logger.debug("start clearing");
+ fixture.clearProvidersLists();
+
+ Object o = new Object();
+ synchronized(o)
+ {
+ o.wait(3000);
+ }
+ }
+
+ /**
+ * An event collector that would collect all events generated by a
+ * provider after a status change. The collector would also do a notidyAll
+ * every time it receives an event.
+ */
+ private class PresenceStatusEventCollector
+ implements ProviderPresenceStatusListener
+ {
+ public ArrayList collectedPresEvents = new ArrayList();
+ public ArrayList collectedStatMsgEvents = new ArrayList();
+
+ public void providerStatusChanged(ProviderPresenceStatusChangeEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedPresEvents.size()+")= "+evt);
+ collectedPresEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ public void providerStatusMessageChanged(PropertyChangeEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected stat.msg. evt("
+ +collectedPresEvents.size()+")= "+evt);
+ collectedStatMsgEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Blocks until at least one event is received or until waitFor
+ * miliseconds pass (whicever happens first).
+ *
+ * @param waitFor the number of miliseconds that we should be waiting
+ * for an event before simply bailing out.
+ */
+ public void waitForPresEvent(long waitFor)
+ {
+ logger.trace("Waiting for a change in provider status.");
+ synchronized(this)
+ {
+ if(collectedPresEvents.size() > 0){
+ logger.trace("Change already received. " + collectedPresEvents);
+ return;
+ }
+
+ try{
+ wait(waitFor);
+ if(collectedPresEvents.size() > 0)
+ logger.trace("Received a change in provider status.");
+ else
+ logger.trace("No change received for "+waitFor+"ms.");
+ }
+ catch (InterruptedException ex){
+ logger.debug("Interrupted while waiting for a provider evt"
+ , ex);
+ }
+ }
+ }
+
+ /**
+ * Blocks until at least one staus message event is received or until
+ * waitFor miliseconds pass (whichever happens first).
+ *
+ * @param waitFor the number of miliseconds that we should be waiting
+ * for a status message event before simply bailing out.
+ */
+ public void waitForStatMsgEvent(long waitFor)
+ {
+ logger.trace("Waiting for a provider status message event.");
+ synchronized(this)
+ {
+ if(collectedStatMsgEvents.size() > 0){
+ logger.trace("Stat msg. evt already received. "
+ + collectedStatMsgEvents);
+ return;
+ }
+
+ try{
+ wait(waitFor);
+ if(collectedStatMsgEvents.size() > 0)
+ logger.trace("Received a prov. stat. msg. evt.");
+ else
+ logger.trace("No prov. stat msg. received for "
+ +waitFor+"ms.");
+ }
+ catch (InterruptedException ex){
+ logger.debug("Interrupted while waiting for a status msg evt"
+ , ex);
+ }
+ }
+ }
+ }
+
+ /**
+ * The class would listen for and store received subscription modification
+ * events.
+ */
+ private class SubscriptionEventCollector implements SubscriptionListener
+ {
+ public ArrayList collectedEvents = new ArrayList();
+
+ /**
+ * Blocks until at least one event is received or until waitFor
+ * miliseconds pass (whicever happens first).
+ *
+ * @param waitFor the number of miliseconds that we should be waiting
+ * for an event before simply bailing out.
+ */
+ public void waitForEvent(long waitFor)
+ {
+ synchronized(this)
+ {
+ if(collectedEvents.size() > 0)
+ return;
+
+ try{
+ wait(waitFor);
+ }
+ catch (InterruptedException ex)
+ {
+ logger.debug(
+ "Interrupted while waiting for a subscription evt", ex);
+ }
+ }
+ }
+
+ /**
+ * Stores the received subsctiption and notifies all waiting on this
+ * object
+ * @param evt the SubscriptionEvent containing the corresponding contact
+ */
+ public void subscriptionCreated(SubscriptionEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Stores the received subsctiption and notifies all waiting on this
+ * object
+ * @param evt the SubscriptionEvent containing the corresponding contact
+ */
+ public void subscriptionRemoved(SubscriptionEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Stores the received subsctiption and notifies all waiting on this
+ * object
+ * @param evt the SubscriptionEvent containing the corresponding contact
+ */
+ public void contactModified(ContactPropertyChangeEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+
+ /**
+ * Stores the received subsctiption and notifies all waiting on this
+ * object
+ * @param evt the SubscriptionEvent containing the corresponding contact
+ */
+ public void subscriptionMoved(SubscriptionMovedEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Stores the received subsctiption and notifies all waiting on this
+ * object
+ * @param evt the SubscriptionEvent containing the corresponding contact
+ */
+ public void subscriptionFailed(SubscriptionEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Stores the received subsctiption and notifies all waiting on this
+ * object
+ * @param evt the SubscriptionEvent containing the corresponding contact
+ */
+ public void subscriptionResolved(SubscriptionEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+
+ }
+
+ /**
+ * The class would listen for and store received events caused by changes
+ * in contact presence states.
+ */
+ private class ContactPresenceEventCollector
+ implements ContactPresenceStatusListener
+ {
+ public ArrayList collectedEvents = new ArrayList();
+ private String trackedScreenName = null;
+ private YahooStatusEnum status = null;
+
+ ContactPresenceEventCollector(String screenname,
+ YahooStatusEnum wantedStatus)
+ {
+ this.trackedScreenName = screenname;
+ this.status = wantedStatus;
+ }
+
+ /**
+ * Blocks until at least one event is received or until waitFor
+ * miliseconds pass (whicever happens first).
+ *
+ * @param waitFor the number of miliseconds that we should be waiting
+ * for an event before simply bailing out.
+ */
+ public void waitForEvent(long waitFor)
+ {
+ synchronized(this)
+ {
+ if(collectedEvents.size() > 0)
+ return;
+
+ try{
+ wait(waitFor);
+ }
+ catch (InterruptedException ex)
+ {
+ logger.debug(
+ "Interrupted while waiting for a subscription evt", ex);
+ }
+ }
+ }
+
+ /**
+ * Stores the received status change event and notifies all waiting on
+ * this object
+ * @param evt the SubscriptionEvent containing the corresponding contact
+ */
+ public void contactPresenceStatusChanged(
+ ContactPresenceStatusChangeEvent evt)
+ {
+ synchronized(this)
+ {
+ //if the user has specified event details and the received
+ //event does not match - then ignore it.
+ if( this.trackedScreenName != null
+ && !evt.getSourceContact().getAddress()
+ .equals(trackedScreenName))
+ return;
+ if( status != null
+ && status != evt.getNewStatus())
+ return;
+
+ logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
+ collectedEvents.add(evt);
+ notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Used to wait till buddy is removed from our contact list.
+ * Used in the authorization process tests
+ */
+ private class UnsubscribeWait implements SubscriptionListener
+ {
+ public void waitForUnsubscribre(long waitFor)
+ {
+ synchronized(this)
+ {
+ try{
+ wait(waitFor);
+ }
+ catch (InterruptedException ex)
+ {
+ logger.debug(
+ "Interrupted while waiting for a subscription evt", ex);
+ }
+ }
+ }
+
+ public void subscriptionRemoved(SubscriptionEvent evt)
+ {
+ synchronized(this)
+ {
+ logger.debug("Got subscriptionRemoved " + evt);
+ notifyAll();
+ }
+ }
+
+ public void subscriptionCreated(SubscriptionEvent evt)
+ {}
+ public void subscriptionFailed(SubscriptionEvent evt)
+ {}
+ public void subscriptionMoved(SubscriptionMovedEvent evt)
+ {}
+ public void subscriptionResolved(SubscriptionEvent evt)
+ {}
+ public void contactModified(ContactPropertyChangeEvent evt)
+ {}
+ }
+
+ /**
+ * AuthorizationHandler which accepts all requests!
+ */
+ private class AuthHandler
+ implements AuthorizationHandler
+ {
+ private OperationSetPresence opset = null;
+ AuthHandler(OperationSetPresence opset)
+ {
+ this.opset = opset;
+ }
+
+ public AuthorizationResponse processAuthorisationRequest(
+ AuthorizationRequest req, Contact sourceContact)
+ {
+ try{
+ opset.subscribe(sourceContact.getAddress());
+ }catch(Exception ex){}
+
+ return
+ new AuthorizationResponse(AuthorizationResponse.ACCEPT, "");
+ }
+ public AuthorizationRequest createAuthorizationRequest(Contact contact )
+ {
+ return new AuthorizationRequest();
+ }
+ public void processAuthorizationResponse(
+ AuthorizationResponse response, Contact sourceContact){}
+ }
+} \ No newline at end of file
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetTypingNotifications.java b/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetTypingNotifications.java
new file mode 100644
index 0000000..1e05ca9
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/TestOperationSetTypingNotifications.java
@@ -0,0 +1,293 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import java.util.*;
+
+import junit.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Tests functionality of the typing notifications operation set.
+ *
+ * @author Damian Minkov
+ */
+public class TestOperationSetTypingNotifications
+ extends TestCase
+{
+ private static final Logger logger =
+ Logger.getLogger(TestOperationSetTypingNotifications.class);
+
+ private YahooSlickFixture fixture = new YahooSlickFixture();
+ private OperationSetTypingNotifications opSetTypingNotifs1 = null;
+ private OperationSetPresence opSetPresence1 = null;
+ private OperationSetTypingNotifications opSetTypingNotifs2 = null;
+ private OperationSetPresence opSetPresence2 = null;
+
+ private OperationSetBasicInstantMessaging opSetBasicIM1 = null;
+ private OperationSetBasicInstantMessaging opSetBasicIM2 = null;
+
+
+ public TestOperationSetTypingNotifications(String name)
+ {
+ super(name);
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ fixture.setUp();
+
+ Map supportedOperationSets1 =
+ fixture.provider1.getSupportedOperationSets();
+
+ if ( supportedOperationSets1 == null
+ || supportedOperationSets1.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ +"this implementation. ");
+
+ //get the operation set presence here.
+ opSetTypingNotifs1 =
+ (OperationSetTypingNotifications)supportedOperationSets1.get(
+ OperationSetTypingNotifications.class.getName());
+
+ //if the op set is null then the implementation doesn't offer a typing.n
+ //operation set which is unacceptable.
+ if (opSetTypingNotifs1 == null)
+ {
+ throw new NullPointerException(
+ "No implementation for typing notifications was found");
+ }
+
+ opSetBasicIM1 =
+ (OperationSetBasicInstantMessaging)supportedOperationSets1.get(
+ OperationSetBasicInstantMessaging.class.getName());
+
+ if (opSetBasicIM1 == null)
+ {
+ throw new NullPointerException(
+ "No implementation for basic IM was found");
+ }
+
+
+ //we also need the presence op set in order to retrieve contacts.
+ opSetPresence1 =
+ (OperationSetPresence)supportedOperationSets1.get(
+ OperationSetPresence.class.getName());
+
+ //if the op set is null show that we're not happy.
+ if (opSetPresence1 == null)
+ {
+ throw new NullPointerException(
+ "An implementation of the service must provide an "
+ + "implementation of at least one of the PresenceOperationSets");
+ }
+
+ Map supportedOperationSets2 =
+ fixture.provider2.getSupportedOperationSets();
+
+ if ( supportedOperationSets2 == null
+ || supportedOperationSets2.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ +"this implementation. ");
+
+ //get the operation set presence here.
+ opSetTypingNotifs2 =
+ (OperationSetTypingNotifications)supportedOperationSets2.get(
+ OperationSetTypingNotifications.class.getName());
+
+ //if the op set is null then the implementation doesn't offer a typing.n
+ //operation set which is unacceptable for.
+ if (opSetTypingNotifs2 == null)
+ {
+ throw new NullPointerException(
+ "No implementation for typing notifications was found");
+ }
+
+ opSetBasicIM2 =
+ (OperationSetBasicInstantMessaging)supportedOperationSets2.get(
+ OperationSetBasicInstantMessaging.class.getName());
+
+ if (opSetBasicIM2 == null)
+ {
+ throw new NullPointerException(
+ "No implementation for basic IM was found");
+ }
+
+
+ //we also need the presence op set in order to retrieve contacts.
+ opSetPresence2 =
+ (OperationSetPresence)supportedOperationSets2.get(
+ OperationSetPresence.class.getName());
+
+ //if the op set is null show that we're not happy.
+ if (opSetPresence2 == null)
+ {
+ throw new NullPointerException(
+ "An implementation of the service must provide an "
+ + "implementation of at least one of the PresenceOperationSets");
+ }
+ }
+
+ /**
+ * Create the list to be sure that contacts exchanging messages
+ * exists in each other lists
+ * @throws Exception
+ */
+ public void prepareContactList() throws Exception
+ {
+ // be sure that contacts are in their lists
+ try{
+ opSetPresence1.subscribe(fixture.userID2);
+ }
+ catch (OperationFailedException ex){
+ // the contact already exist its OK
+ }
+
+ try{
+ opSetPresence2.subscribe(fixture.userID1);
+ }
+ catch (OperationFailedException ex1){
+ // the contact already exist its OK
+ }
+
+ Object o = new Object();
+ synchronized (o)
+ {
+ o.wait(2000);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ fixture.tearDown();
+ }
+
+ /**
+ * Creates a test suite containing tests of this class in a specific order.
+ * We'll first execute a test where we receive a typing notification, and
+ * a volatile contact is created for the sender. we'll then be able to
+ * retrieve this volatile contact and them a notification on our turn.
+ * We need to do things this way as the contact corresponding to the tester
+ * agent has been removed in the previous test and we no longer have it
+ * in our contact list.
+ *
+ * @return Test a testsuite containing all tests to execute.
+ */
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite();
+
+ suite.addTest(new TestOperationSetTypingNotifications(
+ "prepareContactList"));
+
+ //the following 2 need to be run in the specified order.
+ suite.addTest(new TestOperationSetTypingNotifications(
+ "testTypingNotificationsEventDelivery"));
+ return suite;
+ }
+
+ /**
+ * Sends a typing notification and verifies
+ * whether it is properly received by the tested implementation
+ */
+ public void testTypingNotificationsEventDelivery()
+ {
+ TypingEventCollector evtCollector = new TypingEventCollector();
+
+ // send message so request for receiving notifications also to be set
+ Contact notifingContact =
+ opSetPresence1.findContactByID(fixture.userID2);
+ opSetBasicIM1.sendInstantMessage(notifingContact,
+ opSetBasicIM1.createMessage("ping"));
+
+ opSetTypingNotifs1.addTypingNotificationsListener(evtCollector);
+
+ Contact contactToNotify =
+ opSetPresence2.findContactByID(fixture.userID1);
+
+ opSetBasicIM2.sendInstantMessage(contactToNotify,
+ opSetBasicIM2.createMessage("pong"));
+
+ opSetTypingNotifs2.sendTypingNotification(
+ contactToNotify, OperationSetTypingNotifications.STATE_TYPING);
+
+ evtCollector.waitForEvent(10000);
+
+ opSetTypingNotifs1.removeTypingNotificationsListener(evtCollector);
+
+ //check event dispatching
+ assertTrue("Number of typing events received was zero."
+ , evtCollector.collectedEvents.size() > 0);
+
+ TypingNotificationEvent evt = (TypingNotificationEvent)evtCollector
+ .collectedEvents.get(0);
+
+ assertEquals("Source of the typing notification event"
+ , fixture.userID2
+ , evt.getSourceContact().getAddress() );
+
+ assertEquals("Source of the typing notification event"
+ , OperationSetTypingNotifications.STATE_TYPING
+ , evt.getTypingState());
+ }
+
+ /**
+ * Simply collects allre received events and provides a mechanisim for
+ * waiting for the next event.
+ */
+ private class TypingEventCollector implements TypingNotificationsListener
+ {
+ private List collectedEvents = new LinkedList();
+ /**
+ * Called to indicate that a remote <tt>Contact</tt> has sent us a typing
+ * notification. The method adds the <tt>event</tt> to the list of
+ * captured events.
+ * @param event a <tt>TypingNotificationEvent</tt> containing the sender
+ * of the notification and its type.
+ */
+ public void typingNotificationReceifed(TypingNotificationEvent event)
+ {
+ logger.debug("Received a typing notification: " + event);
+ synchronized (this)
+ {
+ collectedEvents.add(event);
+ notifyAll();
+ }
+ }
+
+ /**
+ * Blocks until at least one event is received or until waitFor
+ * miliseconds pass (whicever happens first).
+ *
+ * @param waitFor the number of miliseconds that we should be waiting
+ * for an event before simply bailing out.
+ */
+ public void waitForEvent(long waitFor)
+ {
+ synchronized(this){
+
+ if(collectedEvents.size() > 0)
+ return;
+
+ try{
+ wait(waitFor);
+ }
+ catch (InterruptedException ex){
+ logger.debug(
+ "Interrupted while waiting for a subscription evt", ex);
+ }
+ }
+ }
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/TestProtocolProviderServiceYahooImpl.java b/test/net/java/sip/communicator/slick/protocol/yahoo/TestProtocolProviderServiceYahooImpl.java
new file mode 100644
index 0000000..e5be043
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/TestProtocolProviderServiceYahooImpl.java
@@ -0,0 +1,285 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import java.util.*;
+
+import junit.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Performs testing on protocol provider methods.
+ * @todo add more detailed docs once the tests are written.
+ * @author Damian Minkov
+ */
+public class TestProtocolProviderServiceYahooImpl
+ extends TestCase
+{
+ private static final Logger logger =
+ Logger.getLogger(TestProtocolProviderServiceYahooImpl.class);
+
+ private YahooSlickFixture fixture = new YahooSlickFixture();
+
+ /**
+ * An event adapter that would collec registation state change events
+ */
+ public RegistrationEventCollector regEvtCollector1
+ = new RegistrationEventCollector();
+
+ /**
+ * An event adapter that would collec registation state change events
+ */
+ public RegistrationEventCollector regEvtCollector2
+ = new RegistrationEventCollector();
+
+ /**
+ * Creates a test encapsulator for the method with the specified name.
+ * @param name the name of the method this test should run.
+ */
+ public TestProtocolProviderServiceYahooImpl(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Initializes the fixture.
+ * @throws Exception if super.setUp() throws one.
+ */
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ fixture.setUp();
+ }
+
+ /**
+ * Tears the fixture down.
+ * @throws Exception if fixture.tearDown() fails.
+ */
+ protected void tearDown() throws Exception
+ {
+ fixture.tearDown();
+ super.tearDown();
+ }
+
+ /**
+ * Makes sure that the instance of the Yahoo protocol provider that we're
+ * going to use for testing is properly initialized and registered with
+ * a Yahoo registrar. This MUST be called before any other online testing
+ * of the Yahoo provider so that we won't have to reregister for every single
+ * test.
+ * <p>
+ * The method also verifies that a registration event is fired upon
+ * succesful registration and collected by our event collector.
+ *
+ * @throws OperationFailedException if provider.register() fails.
+ */
+ public void testRegister()
+ throws OperationFailedException
+ {
+ //add an event collector that will collect all events during the
+ //registration and allow us to later inspect them and make sure
+ //they were properly dispatched.
+ fixture.provider1.addRegistrationStateChangeListener(regEvtCollector1);
+ fixture.provider2.addRegistrationStateChangeListener(regEvtCollector2);
+
+ //register both our providers
+ fixture.provider1.register(new SecurityAuthorityImpl(
+ System.getProperty(YahooProtocolProviderServiceLick.ACCOUNT_1_PREFIX
+ + ProtocolProviderFactory.PASSWORD).toCharArray()));
+ fixture.provider2.register(new SecurityAuthorityImpl(
+ System.getProperty(YahooProtocolProviderServiceLick.ACCOUNT_2_PREFIX
+ + ProtocolProviderFactory.PASSWORD).toCharArray()));
+
+ //give it enough time to register. We won't really have to wait all this
+ //time since the registration event collector would notify us the moment
+ //we get signed on.
+ logger.debug("Waiting for registration to complete ...");
+
+ regEvtCollector1.waitForEvent(15000);
+ regEvtCollector2.waitForEvent(40000);
+
+ //make sure that the registration process trigerred the corresponding
+ //events.
+ assertTrue(
+ "No events were dispatched during the registration process."
+ ,regEvtCollector1.collectedNewStates.size() > 0);
+
+ assertTrue(
+ "No registration event notifying of registration was dispatched. "
+ +"All events were: " + regEvtCollector1.collectedNewStates
+ ,regEvtCollector1.collectedNewStates
+ .contains(RegistrationState.REGISTERED));
+
+ //now the same for provider 2
+ assertTrue(
+ "No events were dispatched during the registration process "
+ +"of provider2."
+ ,regEvtCollector2.collectedNewStates.size() > 0);
+
+ assertTrue(
+ "No registration event notifying of registration was dispatched. "
+ +"All events were: " + regEvtCollector2.collectedNewStates
+ ,regEvtCollector2.collectedNewStates
+ .contains(RegistrationState.REGISTERED));
+
+
+ fixture.provider1
+ .removeRegistrationStateChangeListener(regEvtCollector1);
+ fixture.provider2
+ .removeRegistrationStateChangeListener(regEvtCollector2);
+ }
+
+
+ /**
+ * Verifies that all operation sets have the type they are declarded to
+ * have.
+ *
+ * @throws java.lang.Exception if a class indicated in one of the keys
+ * could not be forName()ed.
+ */
+ public void testOperationSetTypes() throws Exception
+ {
+ Map supportedOperationSets
+ = fixture.provider1.getSupportedOperationSets();
+
+ //make sure that keys (which are supposed to be class names) correspond
+ //what the class of the values recorded against them.
+ Iterator setNames = supportedOperationSets.keySet().iterator();
+ while (setNames.hasNext())
+ {
+ String setName = (String) setNames.next();
+ Object opSet = supportedOperationSets.get(setName);
+
+ assertTrue(opSet + " was not an instance of "
+ + setName + " as declared"
+ , Class.forName(setName).isInstance(opSet));
+ }
+ }
+
+ /**
+ * A class that would plugin as a registration listener to a protocol
+ * provider and simply record all events that it sees and notifyAll()
+ * if it sees an event that notifies us of a completed
+ * registration.
+ */
+ public class RegistrationEventCollector
+ implements RegistrationStateChangeListener
+ {
+ public List collectedNewStates = new LinkedList();
+
+ /**
+ * The method would simply register all received events so that they
+ * could be available for later inspection by the unit tests. In the
+ * case where a registraiton event notifying us of a completed
+ * registration is seen, the method would call notifyAll().
+ *
+ * @param evt ProviderStatusChangeEvent the event describing the status
+ * change.
+ */
+ public void registrationStateChanged(RegistrationStateChangeEvent evt)
+ {
+ logger.debug("Received a RegistrationStateChangeEvent: " + evt);
+
+ collectedNewStates.add(evt.getNewState());
+
+ if (evt.getNewState().equals(RegistrationState.REGISTERED))
+ {
+ logger.debug("We're registered and will notify those who wait");
+ synchronized (this)
+ {
+ notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Blocks until an event notifying us of the awaited state change is
+ * received or until waitFor miliseconds pass (whichever happens first).
+ *
+ * @param waitFor the number of miliseconds that we should be waiting
+ * for an event before simply bailing out.
+ */
+ public void waitForEvent(long waitFor)
+ {
+ logger.trace("Waiting for a RegistrationStateChangeEvent ");
+
+ synchronized (this)
+ {
+ if (collectedNewStates.contains(RegistrationState.REGISTERED))
+ {
+ logger.trace("Event already received. "
+ + collectedNewStates);
+ return;
+ }
+
+ try
+ {
+ wait(waitFor);
+
+ if (collectedNewStates.size() > 0)
+ logger.trace(
+ "Received a RegistrationStateChangeEvent.");
+ else
+ logger.trace(
+ "No RegistrationStateChangeEvent received for "
+ + waitFor + "ms.");
+
+ }
+ catch (InterruptedException ex)
+ {
+ logger.debug(
+ "Interrupted while waiting for a "
+ +"RegistrationStateChangeEvent"
+ , ex);
+ }
+ }
+ }
+ }
+
+ /**
+ * A very simple straight forward implementation of a security authority
+ * that would always return the same password (the one specified upon
+ * construction) when asked for credentials.
+ */
+ public class SecurityAuthorityImpl
+ implements SecurityAuthority
+ {
+ /**
+ * The password to return when asked for credentials
+ */
+ private char[] passwd = null;
+
+ /**
+ * Creates an instance of this class that would always return "passwd"
+ * when asked for credentials.
+ *
+ * @param passwd the password that this class should return when
+ * asked for credentials.
+ */
+ public SecurityAuthorityImpl(char[] passwd)
+ {
+ this.passwd = passwd;
+ }
+
+ /**
+ * Returns a Credentials object associated with the specified realm.
+ * <p>
+ * @param realm The realm that the credentials are needed for.
+ * @param defaultValues the values to propose the user by default
+ * @return The credentials associated with the specified realm or null
+ * if none could be obtained.
+ */
+ public UserCredentials obtainCredentials(String realm,
+ UserCredentials defaultValues)
+ {
+ defaultValues.setPassword(passwd);
+ return defaultValues;
+ }
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/YahooProtocolProviderServiceLick.java b/test/net/java/sip/communicator/slick/protocol/yahoo/YahooProtocolProviderServiceLick.java
new file mode 100644
index 0000000..49765b1
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/YahooProtocolProviderServiceLick.java
@@ -0,0 +1,106 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import java.util.*;
+
+import org.osgi.framework.*;
+import junit.framework.*;
+
+/**
+ * Yahoo specific testing for a Yahoo Protocol Provider Service implementation.
+ * The test suite registers two accounts for
+ *
+ * @author Damian Minkov
+ */
+public class YahooProtocolProviderServiceLick
+ extends TestSuite
+ implements BundleActivator
+{
+ /**
+ * The prefix used for property names containing settings for our first
+ * testing account.
+ */
+ public static final String ACCOUNT_1_PREFIX
+ = "accounts.yahoo.account1.";
+
+ /**
+ * The prefix used for property names containing settings for our second
+ * testing account.
+ */
+ public static final String ACCOUNT_2_PREFIX
+ = "accounts.yahoo.account2.";
+
+ /**
+ * The name of the property that indicates whether the user would like to
+ * only run the offline tests.
+ */
+ public static final String DISABLE_ONLINE_TESTS_PROPERTY_NAME
+ = "accounts.yahoo.DISABLE_ONLINE_TESTING";
+
+ /**
+ * The name of the property the value of which is a formatted string that
+ * contains the contact list that.
+ */
+ public static final String CONTACT_LIST_PROPERTY_NAME
+ = "accounts.yahoo.CONTACT_LIST";
+
+ /**
+ * Initializes and registers all tests that we'll run as a part of this
+ * slick.
+ *
+ * @param context a currently valid bundle context.
+ */
+ public void start(BundleContext context)
+ {
+ setName("YahooProtocolProviderSlick");
+
+ Hashtable properties = new Hashtable();
+ properties.put("service.pid", getName());
+
+ YahooSlickFixture.bc = context;
+
+ // verify whether the user wants to avoid online testing
+ String offlineMode = System.getProperty(
+ DISABLE_ONLINE_TESTS_PROPERTY_NAME, null);
+
+ if (offlineMode != null && offlineMode.equalsIgnoreCase("true"))
+ YahooSlickFixture.onlineTestingDisabled = true;
+
+
+ addTestSuite(TestAccountInstallation.class);
+ addTestSuite(TestProtocolProviderServiceYahooImpl.class);
+
+ addTest(TestOperationSetPresence.suite());
+
+ //the following should only be run when we want online testing.
+ if(!YahooSlickFixture.onlineTestingDisabled)
+ {
+ addTest(TestOperationSetPersistentPresence.suite());
+
+ addTest(TestOperationSetBasicInstantMessaging.suite());
+
+ // Sending typing notifications doesn't work for now
+ //addTest(TestOperationSetTypingNotifications.suite());
+ }
+
+ addTest(TestAccountUninstallation.suite());
+ addTestSuite(TestAccountUninstallationPersistence.class);
+
+ context.registerService(getClass().getName(), this, properties);
+ }
+
+ /**
+ * Prepares the slick for shutdown.
+ *
+ * @param context a currently valid bundle context.
+ */
+ public void stop(BundleContext context)
+ {
+
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/YahooSlickFixture.java b/test/net/java/sip/communicator/slick/protocol/yahoo/YahooSlickFixture.java
new file mode 100644
index 0000000..1a62418
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/YahooSlickFixture.java
@@ -0,0 +1,295 @@
+/*
+ * 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.slick.protocol.yahoo;
+
+import org.osgi.framework.*;
+import junit.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import java.util.Map;
+import java.util.*;
+
+/**
+ * Contains fields and methods used by most or all tests in the yahoo slick.
+ *
+ * @author Damian Minkov
+ */
+public class YahooSlickFixture
+ extends TestCase
+{
+ /**
+ * To be set by the slick itself upon activation.
+ */
+ public static BundleContext bc = null;
+
+ /**
+ * An osgi service reference for the protocol provider corresponding to our
+ * first testing account.
+ */
+ public ServiceReference provider1ServiceRef = null;
+
+ /**
+ * The protocol provider corresponding to our first testing account.
+ */
+ public ProtocolProviderService provider1 = null;
+
+ /**
+ * The user ID associated with testing account 1.
+ */
+ public String userID1 = null;
+
+ /**
+ * An osgi service reference for the protocol provider corresponding to our
+ * second testing account.
+ */
+ public ServiceReference provider2ServiceRef = null;
+
+ /**
+ * The protocol provider corresponding to our first testing account.
+ */
+ public ProtocolProviderService provider2 = null;
+
+ /**
+ * The user ID associated with testing account 2.
+ */
+ public String userID2 = null;
+
+
+ /**
+ * The tested protocol provider factory.
+ */
+ public ProtocolProviderFactory providerFactory = null;
+
+ /**
+ * A reference to the bundle containing the tested pp implementation. This
+ * reference is set during the accoung uninstallation testing and used during
+ * the account uninstallation persistence testing.
+ */
+ public static Bundle providerBundle = null;
+
+ /**
+ * Indicates whether the user has requested for onlline tests not to be run.
+ * (e.g. due to lack of network connectivity or ... time constraints ;)).
+ */
+ public static boolean onlineTestingDisabled = false;
+
+ /**
+ * A Hashtable containing group names mapped against array lists of buddy
+ * screen names. This is a snapshot of the server stored buddy list for
+ * the account that is going to be used by the tested implementation.
+ * It is filled in by the tester agent who'd login with that account
+ * and initialise the ss contact list before the tested implementation has
+ * actually done so.
+ */
+ public static Hashtable preInstalledBuddyList = null;
+
+
+ /**
+ * Initializes protocol provider references and whatever else there is to
+ * initialize.
+ *
+ * @throws java.lang.Exception in case we meet problems while retriving
+ * protocol providers through OSGI
+ */
+ public void setUp()
+ throws Exception
+ {
+ // first obtain a reference to the provider factory
+ ServiceReference[] serRefs = null;
+ String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL
+ + "="+ProtocolNames.YAHOO+")";
+ try{
+ serRefs = bc.getServiceReferences(
+ ProtocolProviderFactory.class.getName(), osgiFilter);
+ }
+ catch (InvalidSyntaxException ex){
+ //this really shouldhn't occur as the filter expression is static.
+ fail(osgiFilter + " is not a valid osgi filter");
+ }
+
+ assertTrue(
+ "Failed to find a provider factory service for protocol yahoo",
+ serRefs != null || serRefs.length > 0);
+
+ //Keep the reference for later usage.
+ providerFactory = (ProtocolProviderFactory)bc.getService(serRefs[0]);
+
+ userID1 =
+ System.getProperty(
+ YahooProtocolProviderServiceLick.ACCOUNT_1_PREFIX
+ + ProtocolProviderFactory.USER_ID);
+
+ userID2 =
+ System.getProperty(
+ YahooProtocolProviderServiceLick.ACCOUNT_2_PREFIX
+ + ProtocolProviderFactory.USER_ID);
+
+ //find the protocol providers exported for the two accounts
+ ServiceReference[] yahooProvider1Refs
+ = bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(&"
+ +"("+ProtocolProviderFactory.PROTOCOL+"="+ProtocolNames.YAHOO+")"
+ +"("+ProtocolProviderFactory.USER_ID+"="
+ + userID1 +")"
+ +")");
+
+ //make sure we found a service
+ assertNotNull("No Protocol Provider was found for yahoo account1:"
+ + userID1
+ , yahooProvider1Refs);
+ assertTrue("No Protocol Provider was found for yahoo account1:"+ userID1,
+ yahooProvider1Refs.length > 0);
+
+ ServiceReference[] yahooProvider2Refs
+ = bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(&"
+ +"("+ProtocolProviderFactory.PROTOCOL+"="+ProtocolNames.YAHOO+")"
+ +"("+ProtocolProviderFactory.USER_ID+"="
+ + userID2 +")"
+ +")");
+
+ //again make sure we found a service.
+ assertNotNull("No Protocol Provider was found for yahoo account2:"
+ + userID2
+ , yahooProvider2Refs);
+ assertTrue("No Protocol Provider was found for yahoo account2:"+ userID2,
+ yahooProvider2Refs.length > 0);
+
+ //save the service for other tests to use.
+ provider1ServiceRef = yahooProvider1Refs[0];
+ provider1 = (ProtocolProviderService)bc.getService(provider1ServiceRef);
+ provider2ServiceRef = yahooProvider2Refs[0];
+ provider2 = (ProtocolProviderService)bc.getService(provider2ServiceRef);
+ }
+
+ /**
+ * Un get service references used in here.
+ */
+ public void tearDown()
+ {
+ bc.ungetService(provider1ServiceRef);
+ bc.ungetService(provider2ServiceRef);
+ }
+
+ /**
+ * Returns the bundle that has registered the protocol provider service
+ * implementation that we're currently testing. The method would go through
+ * all bundles currently installed in the framework and return the first
+ * one that exports the same protocol provider instance as the one we test
+ * in this slick.
+ * @param provider the provider whose bundle we're looking for.
+ * @return the Bundle that has registered the protocol provider service
+ * we're testing in the slick.
+ */
+ public static Bundle findProtocolProviderBundle(
+ ProtocolProviderService provider)
+ {
+ Bundle[] bundles = bc.getBundles();
+
+ for (int i = 0; i < bundles.length; i++)
+ {
+ ServiceReference[] registeredServices
+ = bundles[i].getRegisteredServices();
+
+ if (registeredServices == null)
+ continue;
+
+ for (int j = 0; j < registeredServices.length; j++)
+ {
+ Object service
+ = bc.getService(registeredServices[j]);
+ if (service == provider)
+ return bundles[i];
+ }
+ }
+
+ return null;
+ }
+
+ public void clearProvidersLists()
+ throws Exception
+ {
+ Map supportedOperationSets1 = provider1.getSupportedOperationSets();
+
+ if ( supportedOperationSets1 == null
+ || supportedOperationSets1.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ +"this yahoo implementation. ");
+
+ //get the operation set presence here.
+ OperationSetPersistentPresence opSetPersPresence1 =
+ (OperationSetPersistentPresence)supportedOperationSets1.get(
+ OperationSetPersistentPresence.class.getName());
+
+ //if still null then the implementation doesn't offer a presence
+ //operation set which is unacceptable for yahoo.
+ if (opSetPersPresence1 == null)
+ throw new NullPointerException(
+ "An implementation of the yahoo service must provide an "
+ + "implementation of at least the one of the Presence "
+ + "Operation Sets");
+
+ // lets do it once again for the second provider
+ Map supportedOperationSets2 = provider2.getSupportedOperationSets();
+
+ if (supportedOperationSets2 == null
+ || supportedOperationSets2.size() < 1)
+ throw new NullPointerException(
+ "No OperationSet implementations are supported by "
+ + "this yahoo implementation. ");
+
+ //get the operation set presence here.
+ OperationSetPersistentPresence opSetPersPresence2 =
+ (OperationSetPersistentPresence) supportedOperationSets2.get(
+ OperationSetPersistentPresence.class.getName());
+
+ //if still null then the implementation doesn't offer a presence
+ //operation set which is unacceptable for yahoo.
+ if (opSetPersPresence2 == null)
+ throw new NullPointerException(
+ "An implementation of the yahoo service must provide an "
+ + "implementation of at least the one of the Presence "
+ + "Operation Sets");
+
+ ContactGroup rootGroup1 = opSetPersPresence1.getServerStoredContactListRoot();
+
+ // first delete the groups
+ Vector groupsToRemove = new Vector();
+ Iterator iter = rootGroup1.subgroups();
+ while (iter.hasNext())
+ {
+ groupsToRemove.add(iter.next());
+ }
+
+ iter = groupsToRemove.iterator();
+ while (iter.hasNext())
+ {
+ ContactGroup item = (ContactGroup) iter.next();
+ opSetPersPresence1.removeServerStoredContactGroup(item);
+ }
+
+ ContactGroup rootGroup2 = opSetPersPresence2.getServerStoredContactListRoot();
+
+ // delete groups
+ groupsToRemove = new Vector();
+ iter = rootGroup2.subgroups();
+ while (iter.hasNext())
+ {
+ groupsToRemove.add(iter.next());
+ }
+
+ iter = groupsToRemove.iterator();
+ while (iter.hasNext())
+ {
+ ContactGroup item = (ContactGroup) iter.next();
+ opSetPersPresence2.removeServerStoredContactGroup(item);
+ }
+
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/yahoo/yahoo.provider.slick.manifest.mf b/test/net/java/sip/communicator/slick/protocol/yahoo/yahoo.provider.slick.manifest.mf
new file mode 100644
index 0000000..f8a0a77
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/yahoo/yahoo.provider.slick.manifest.mf
@@ -0,0 +1,15 @@
+Bundle-Activator: net.java.sip.communicator.slick.protocol.yahoo.YahooProtocolProviderServiceLick
+Bundle-Name: Yahoo Protocol Provider Service Leveraging Implementation Compatibility Kit
+Bundle-Description: A Service Leveraging Implementation Compatibility Kit for the Yahoo implementation of the ProtocolProvider Service
+Bundle-Vendor: sip-communicator.org
+Bundle-Version: 0.0.1
+Import-Package: net.java.sip.communicator.service.configuration,
+ net.java.sip.communicator.service.configuration.event,
+ junit.framework,
+ org.osgi.framework,
+ javax.net.ssl,
+ javax.xml.parsers,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.service.protocol,
+ net.java.sip.communicator.service.protocol.yahooconstants,
+ net.java.sip.communicator.service.protocol.event