aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java
diff options
context:
space:
mode:
authorDamian Minkov <damencho@jitsi.org>2007-06-01 15:45:11 +0000
committerDamian Minkov <damencho@jitsi.org>2007-06-01 15:45:11 +0000
commit03262f0dd4b61a20d5e13832e70a10e8b3889b7b (patch)
treed03f92e081236e5307d8f6902b28ded71d665ae8 /src/net/java
parent2dfda989832e5daf4a72a39a2b41f23a533ef806 (diff)
downloadjitsi-03262f0dd4b61a20d5e13832e70a10e8b3889b7b.zip
jitsi-03262f0dd4b61a20d5e13832e70a10e8b3889b7b.tar.gz
jitsi-03262f0dd4b61a20d5e13832e70a10e8b3889b7b.tar.bz2
RSS ProtocolProvider.
Diffstat (limited to 'src/net/java')
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/ContactGroupRssImpl.java620
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/ContactRssImpl.java428
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/MessageRssImpl.java137
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java436
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/OperationSetPersistentPresenceRssImpl.java1357
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/OperationSetTypingNotificationsRssImpl.java193
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/ProtocolIconRssImpl.java102
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/ProtocolProviderFactoryRssImpl.java272
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/ProtocolProviderServiceRssImpl.java447
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/RssAccountID.java32
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/RssActivator.java116
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/RssFeedReader.java256
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/RssStatusEnum.java104
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/RssThread.java73
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/RssTimerRefreshFeed.java41
-rw-r--r--src/net/java/sip/communicator/impl/protocol/rss/rss.provider.manifest.mf15
-rw-r--r--src/net/java/sip/communicator/plugin/rssaccregwizz/FirstWizardPage.java265
-rw-r--r--src/net/java/sip/communicator/plugin/rssaccregwizz/Resources.java95
-rw-r--r--src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccRegWizzActivator.java109
-rw-r--r--src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccountRegistration.java82
-rw-r--r--src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccountRegistrationWizard.java198
-rw-r--r--src/net/java/sip/communicator/plugin/rssaccregwizz/resources.properties10
-rw-r--r--src/net/java/sip/communicator/plugin/rssaccregwizz/rssaccregwizz.manifest.mf30
23 files changed, 5418 insertions, 0 deletions
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/ContactGroupRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/ContactGroupRssImpl.java
new file mode 100644
index 0000000..8546d93
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/ContactGroupRssImpl.java
@@ -0,0 +1,620 @@
+/*
+ * 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.rss;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * A simple, straightforward implementation of a rss ContactGroup. Since
+ * the Rss protocol is not a real one, we simply store all group details
+ * in class fields. You should know that when implementing a real protocol,
+ * the contact group implementation would rather encapsulate group objects from
+ * the protocol stack and group property values should be returned by consulting
+ * the encapsulated object.
+ *
+ * @author Emil Ivov
+ */
+public class ContactGroupRssImpl
+ implements ContactGroup
+{
+ private static final Logger logger
+ = Logger.getLogger(ContactGroupRssImpl.class);
+
+ /**
+ * The name of this Rss contact group.
+ */
+ private String groupName = null;
+
+ /**
+ * The list of this group's members.
+ */
+ private Vector contacts = new Vector();
+
+ /**
+ * The list of sub groups belonging to this group.
+ */
+ private Vector subGroups = new Vector();
+
+ /**
+ * The group that this group belongs to (or null if this is the root group).
+ */
+ private ContactGroupRssImpl parentGroup = null;
+
+ /**
+ * Determines whether this group is really in the contact list or whether
+ * it is here only temporarily and will be gone next time we restart.
+ */
+ private boolean isPersistent = true;
+
+ /**
+ * The protocol provider that created us.
+ */
+ private ProtocolProviderServiceRssImpl parentProvider = null;
+
+ /**
+ * Determines whether this group has been resolved on the server.
+ * Unresolved groups are groups that were available on previous runs and
+ * that the meta contact list has stored. During all next runs, when
+ * bootstrapping, the meta contact list would create these groups as
+ * unresolved. Once a protocol provider implementation confirms that the
+ * groups are still on the server, it would issue an event indicating that
+ * the groups are now resolved.
+ */
+ private boolean isResolved = true;
+
+ /**
+ * An id uniquely identifying the group. For many protocols this could be
+ * the group name itself.
+ */
+ private String uid = null;
+ private static final String UID_SUFFIX = ".uid";
+
+ /**
+ * Creates a ContactGroupRssImpl with the specified name.
+ *
+ * @param groupName the name of the group.
+ * @param parentProvider the protocol provider that created this group.
+ */
+ public ContactGroupRssImpl(
+ String groupName,
+ ProtocolProviderServiceRssImpl parentProvider)
+ {
+ this.groupName = groupName;
+ this.uid = groupName + UID_SUFFIX;
+ this.parentProvider = parentProvider;
+ }
+
+ /**
+ * Determines whether the group may contain subgroups or not.
+ *
+ * @return always true in this implementation.
+ */
+ public boolean canContainSubgroups()
+ {
+ 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 parentProvider;
+ }
+
+ /**
+ * 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 contacts.iterator();
+ }
+
+ /**
+ * Adds the specified contact to this group.
+ * @param contactToAdd the ContactRssImpl to add to this group.
+ */
+ public void addContact(ContactRssImpl contactToAdd)
+ {
+ this.contacts.add(contactToAdd);
+ contactToAdd.setParentGroup(this);
+ }
+
+ /**
+ * 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 contacts.size();
+ }
+
+ /**
+ * Returns the number of subgroups contained by this
+ * <tt>ContactGroup</tt>.
+ *
+ * @return the number of subGroups currently added to this group.
+ */
+ public int countSubgroups()
+ {
+ return subGroups.size();
+ }
+
+ /**
+ * Adds the specified contact group to the contained by this group.
+ * @param subgroup the ContactGroupRssImpl to add as a subgroup to this group.
+ */
+ public void addSubgroup(ContactGroupRssImpl subgroup)
+ {
+ this.subGroups.add(subgroup);
+ subgroup.setParentGroup(this);
+ }
+
+ /**
+ * Sets the group that is the new parent of this group
+ * @param parent ContactGroupRssImpl
+ */
+ void setParentGroup(ContactGroupRssImpl parent)
+ {
+ this.parentGroup = parent;
+ }
+
+ /**
+ * Returns the contact group that currently contains this group or null if
+ * this is the root contact group.
+ * @return the contact group that currently contains this group or null if
+ * this is the root contact group.
+ */
+ public ContactGroup getParentContactGroup()
+ {
+ return this.parentGroup;
+ }
+
+ /**
+ * Removes the specified contact group from the this group's subgroups.
+ * @param subgroup the ContactGroupRssImpl subgroup to remove.
+ */
+ public void removeSubGroup(ContactGroupRssImpl subgroup)
+ {
+ this.subGroups.remove(subgroup);
+ subgroup.setParentGroup(null);
+ }
+
+
+ /**
+ * 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 (ContactRssImpl)contacts.get(index);
+ }
+
+ /**
+ * Returns the group that is parent of the specified rssGroup or null
+ * if no parent was found.
+ * @param rssGroup the group whose parent we're looking for.
+ * @return the ContactGroupRssImpl instance that rssGroup
+ * belongs to or null if no parent was found.
+ */
+ public ContactGroupRssImpl findGroupParent(
+ ContactGroupRssImpl rssGroup)
+ {
+ if ( subGroups.contains(rssGroup) )
+ return this;
+
+ Iterator subGroupsIter = subgroups();
+ while (subGroupsIter.hasNext())
+ {
+ ContactGroupRssImpl subgroup
+ = (ContactGroupRssImpl) subGroupsIter.next();
+
+ ContactGroupRssImpl parent
+ = subgroup.findGroupParent(rssGroup);
+
+ if(parent != null)
+ return parent;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the group that is parent of the specified rssContact or
+ * null if no parent was found.
+ *
+ * @param rssContact the contact whose parent we're looking for.
+ * @return the ContactGroupRssImpl instance that rssContact
+ * belongs to or <tt>null</tt> if no parent was found.
+ */
+ public ContactGroupRssImpl findContactParent(
+ ContactRssImpl rssContact)
+ {
+ if ( contacts.contains(rssContact) )
+ return this;
+
+ Iterator subGroupsIter = subgroups();
+ while (subGroupsIter.hasNext())
+ {
+ ContactGroupRssImpl subgroup
+ = (ContactGroupRssImpl) subGroupsIter.next();
+
+ ContactGroupRssImpl parent
+ = subgroup.findContactParent(rssContact);
+
+ if(parent != null)
+ return parent;
+ }
+ 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)
+ {
+ Iterator contactsIter = contacts();
+ while (contactsIter.hasNext())
+ {
+ ContactRssImpl contact = (ContactRssImpl) contactsIter.next();
+ if (contact.getAddress().equals(id))
+ return contact;
+
+ }
+ 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 (ContactGroup)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 groupsIter = subgroups();
+ while (groupsIter.hasNext())
+ {
+ ContactGroupRssImpl contactGroup
+ = (ContactGroupRssImpl) groupsIter.next();
+ if (contactGroup.getGroupName().equals(groupName))
+ return contactGroup;
+
+ }
+ return null;
+
+ }
+
+ /**
+ * Returns the name of this group.
+ *
+ * @return a String containing the name of this group.
+ */
+ public String getGroupName()
+ {
+ return this.groupName;
+ }
+
+ /**
+ * Sets this group a new name.
+ * @param newGrpName a String containing the new name of this group.
+ */
+ public void setGroupName(String newGrpName)
+ {
+ this.groupName = newGrpName;
+ }
+
+ /**
+ * 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();
+ }
+
+ /**
+ * Removes the specified contact from this group.
+ * @param contact the ContactRssImpl to remove from this group
+ */
+ public void removeContact(ContactRssImpl contact)
+ {
+ this.contacts.remove(contact);
+ }
+
+ /**
+ * Returns the contact with the specified id or null if no such contact
+ * exists.
+ * @param id the id of the contact we're looking for.
+ * @return ContactRssImpl
+ */
+ public ContactRssImpl findContactByID(String id)
+ {
+ //first go through the contacts that are direct children.
+ Iterator contactsIter = contacts();
+
+ while(contactsIter.hasNext())
+ {
+ ContactRssImpl mContact = (ContactRssImpl)contactsIter.next();
+
+ if( mContact.getAddress().equals(id) )
+ return mContact;
+ }
+
+ //if we didn't find it here, let's try in the subougroups
+ Iterator groupsIter = subgroups();
+
+ while( groupsIter.hasNext() )
+ {
+ ContactGroupRssImpl mGroup = (ContactGroupRssImpl)groupsIter.next();
+
+ ContactRssImpl mContact = mGroup.findContactByID(id);
+
+ if (mContact != null)
+ return mContact;
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Returns a String representation of this group and the contacts it
+ * contains (may turn out to be a relatively long string).
+ * @return a String representing this group and its child contacts.
+ */
+ public String toString()
+ {
+
+ StringBuffer buff = new StringBuffer(getGroupName());
+ buff.append(".subGroups=" + countSubgroups() + ":\n");
+
+ Iterator subGroups = subgroups();
+ while (subGroups.hasNext())
+ {
+ ContactGroupRssImpl group = (ContactGroupRssImpl)subGroups.next();
+ buff.append(group.toString());
+ if (subGroups.hasNext())
+ buff.append("\n");
+ }
+
+ buff.append("\nChildContacts="+countContacts()+":[");
+
+ Iterator contacts = contacts();
+ while (contacts.hasNext())
+ {
+ ContactRssImpl contact = (ContactRssImpl) contacts.next();
+ buff.append(contact.toString());
+ if(contacts.hasNext())
+ buff.append(", ");
+ }
+ return buff.append("]").toString();
+ }
+
+ public Vector getRssURLList(Vector rssURLList){
+ //private Vector rssURLList;
+ //StringBuffer buff = new StringBuffer(getGroupName());
+ //buff.append(".subGroups=" + countSubgroups() + ":\n");
+
+ Iterator subGroups = subgroups();
+ while (subGroups.hasNext())
+ {
+ ContactGroupRssImpl group = (ContactGroupRssImpl)subGroups.next();
+ //buff.append(
+ group.getRssURLList(rssURLList);
+ // if (subGroups.hasNext())
+ // buff.append("\n");
+ }
+
+ //buff.append("\nChildContacts="+countContacts()+":[");
+
+ Iterator contacts = contacts();
+ while (contacts.hasNext())
+ {
+ ContactRssImpl contact = (ContactRssImpl) contacts.next();
+ //buff.append(contact.getDisplayName());
+ rssURLList.addElement(contact);
+ //if(contacts.hasNext())
+ // buff.append(", ");
+ }
+ //return buff.append("]").toString();
+ return rssURLList;
+ }
+
+ /**
+ * Specifies whether or not this contact group is being stored by the server.
+ * Non persistent contact groups 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 and the contact that we
+ * associated with that user is placed in a non persistent group. Non
+ * persistent contact groups 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.
+ *
+ * @param isPersistent true if the contact group is to be persistent and
+ * false otherwise.
+ */
+ public void setPersistent(boolean isPersistent)
+ {
+ this.isPersistent = isPersistent;
+ }
+
+ /**
+ * 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 isPersistent;
+ }
+
+ /**
+ * 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 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;
+ }
+
+ /**
+ * Makes the group resolved or unresolved.
+ *
+ * @param resolved true to make the group resolved; false to
+ * make it unresolved
+ */
+ public void setResolved(boolean resolved)
+ {
+ this.isResolved = resolved;
+ }
+
+ /**
+ * Returns a <tt>String</tt> that uniquely represnets the group inside
+ * the current protocol. The string MUST be persistent (it must not change
+ * across connections or runs of the application). In many cases (Jabber,
+ * ICQ) the string may match the name of the group as these protocols
+ * only allow a single level of contact groups and there is no danger of
+ * having the same name twice in the same contact list. Other protocols
+ * (no examples come to mind but that doesn't bother me ;) ) may be
+ * supporting mutilple levels of grooups so it might be possible for group
+ * A and group B to both contain groups named C. In such cases the
+ * implementation must find a way to return a unique identifier in this
+ * method and this UID should never change for a given group.
+ *
+ * @return a String representing this group in a unique and persistent
+ * way.
+ */
+ public String getUID()
+ {
+ return uid;
+ }
+
+ /**
+ * Ugly but tricky conversion method.
+ * @param uid the uid we'd like to get a name from
+ * @return the name of the group with the specified <tt>uid</tt>.
+ */
+ static String createNameFromUID(String uid)
+ {
+ return uid.substring(0, uid.length() - (UID_SUFFIX.length()));
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one which in terms
+ * of contact groups translates to having the equal names and matching
+ * subgroups and child contacts. The resolved status of contactgroups and
+ * contacts is deliberately ignored so that groups and/or contacts would
+ * be assumed equal even if it differs.
+ * <p>
+ * @param obj the reference object with which to compare.
+ * @return <code>true</code> if this contact group has the equal child
+ * contacts and subgroups to those of the <code>obj</code> argument.
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj == null
+ || !(obj instanceof ContactGroupRssImpl))
+ return false;
+
+ ContactGroupRssImpl rssGroup
+ = (ContactGroupRssImpl)obj;
+
+ if( ! rssGroup.getGroupName().equals(getGroupName())
+ || ! rssGroup.getUID().equals(getUID())
+ || rssGroup.countContacts() != countContacts()
+ || rssGroup.countSubgroups() != countSubgroups())
+ return false;
+
+ //traverse child contacts
+ Iterator theirContacts = rssGroup.contacts();
+
+ while(theirContacts.hasNext())
+ {
+ ContactRssImpl theirContact
+ = (ContactRssImpl)theirContacts.next();
+
+ ContactRssImpl ourContact
+ = (ContactRssImpl)getContact(theirContact.getAddress());
+
+ if(ourContact == null
+ || !ourContact.equals(theirContact))
+ return false;
+ }
+
+ //traverse subgroups
+ Iterator theirSubgroups = rssGroup.subgroups();
+
+ while(theirSubgroups.hasNext())
+ {
+ ContactGroupRssImpl theirSubgroup
+ = (ContactGroupRssImpl)theirSubgroups.next();
+
+ ContactGroupRssImpl ourSubgroup
+ = (ContactGroupRssImpl)getGroup(
+ theirSubgroup.getGroupName());
+
+ if(ourSubgroup == null
+ || !ourSubgroup.equals(theirSubgroup))
+ return false;
+ }
+
+ return true;
+ }
+}
+
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/ContactRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/ContactRssImpl.java
new file mode 100644
index 0000000..ae9428c
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/ContactRssImpl.java
@@ -0,0 +1,428 @@
+/*
+ * 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.rss;
+
+import java.util.*;
+import java.text.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * An implementation of a rss Contact.
+ *
+ * @author Jean-Albert Vescovo
+ */
+public class ContactRssImpl
+ implements Contact
+{
+ private String lastDate = null;
+ private Date date = null;
+ private String nickName = null;
+
+ private static final Logger logger
+ = Logger.getLogger(ContactRssImpl.class);
+
+ private static SimpleDateFormat DATE_FORMATTER =
+ new SimpleDateFormat("yyyy.MM.dd-HH:mm:ss");
+
+ /**
+ * The id of the contact.
+ */
+ private String contactID = null;
+
+ /**
+ * The provider that created us.
+ */
+ private ProtocolProviderServiceRssImpl parentProvider = null;
+
+ /**
+ * The group that belong to.
+ */
+ private ContactGroupRssImpl parentGroup = null;
+
+ /**
+ * The presence status of the contact.
+ */
+ private PresenceStatus presenceStatus = RssStatusEnum.ONLINE;
+
+ /**
+ * Determines whether this contact is persistent, i.e. member of the contact
+ * list or whether it is here only temporarily.
+ */
+ private boolean isPersistent = true;
+
+ /**
+ * Determines whether the contact has been resolved (i.e. we have a
+ * confirmation that it is still on the server contact list).
+ */
+ private boolean isResolved = false;
+
+ /**
+ * Creates an instance of a meta contact with the specified string used
+ * as a name and identifier.
+ *
+ * @param id the identifier of this contact (also used as a name).
+ * @param parentProvider the provider that created us.
+ */
+ public ContactRssImpl(
+ String id,
+ ProtocolProviderServiceRssImpl parentProvider)
+ {
+ this.contactID = id;
+ this.parentProvider = parentProvider;
+ }
+
+ /**
+ * This method is only called when the contact is added to a new
+ * <tt>ContactGroupRssImpl</tt> by the
+ * <tt>ContactGroupRssImpl</tt> itself.
+ *
+ * @param newParentGroup the <tt>ContactGroupRssImpl</tt> that is now
+ * parent of this <tt>ContactRssImpl</tt>
+ */
+ void setParentGroup(ContactGroupRssImpl newParentGroup)
+ {
+ this.parentGroup = newParentGroup;
+ }
+
+ /**
+ * Returns a String that can be used for identifying the contact.
+ *
+ * @return a String id representing and uniquely identifying the contact.
+ */
+ public String getAddress()
+ {
+ return contactID;
+ }
+
+ /**
+ * Returns a String that could be used by any user interacting modules
+ * for referring to this contact.
+ *
+ * @return a String that can be used for referring to this contact when
+ * interacting with the user.
+ */
+ public String getDisplayName()
+ {
+ if(nickName == null) return contactID;
+ else return nickName;
+ }
+
+ public void setDisplayName(String nickName){
+ this.nickName = nickName;
+ }
+
+ /**
+ * Returns a Date corresponding to the date of the last query
+ * on this rss contact.
+ *
+ * @return a Date in order to compare with a new one obtained via
+ * a query on the feed.
+ */
+ public Date getDate()
+ {
+ return this.date;
+ }
+
+ /**
+ * This method is only called when a new date is found after a query
+ * on the feed corresponding to this contact
+ *
+ * @param date the <tt>Date</tt> that is now
+ * the last update date of the <tt>ContactRssImpl</tt>
+ */
+ public void setDate(Date date)
+ {
+ this.date = date;
+ this.lastDate = convertDateToString(this.date);
+ }
+
+ /**
+ * Updating the lastDate in String format of the contact
+ *
+ * @param lastDate the <tt>String</tt> that is now
+ * the last update date of the <tt>ContactRssImpl</tt>
+ */
+ public void setLastDate(String lastDate)
+ {
+ this.lastDate = lastDate;
+ }
+
+ /**
+ * Returns a String corresponding to the date of the last query
+ * on this rss contact.
+ *
+ * @return a String representing a Date in order to compare with
+ * a new one obtained via a query on the feed.
+ */
+ public String getLastDate()
+ {
+ return this.lastDate;
+ }
+
+ /**
+ * Returns a String corresponding to a date after a conversion
+ * from a Date
+ *
+ * @param date the date
+ * @return a String which is placed in the lastDate variable of the
+ * present contact
+ */
+ private String convertDateToString(Date date)
+ {
+ return DATE_FORMATTER.format(date);
+ }
+
+ /**
+ * This method is called when a the contact is restored and a
+ * previous saved lastDate is found as persistent-data: this
+ * data is in a String format, and this method convert it into
+ * a Date usable by the protocol.
+ * @param lastDate date as String
+ */
+ private void convertStringToDate(String lastDate)
+ {
+ try
+ {
+ this.date = DATE_FORMATTER.parse(lastDate);
+ }
+ catch(ParseException ex)
+ {
+ logger.error("Cannot parse Date", ex);
+ }
+ }
+
+ /**
+ * Returns an array of String corresponding to a date bursted in multiple
+ * fields as this:
+ * ddd mmm DD HH:mm:ss ZZZZ YYYY
+ *
+ * @return an Array of String
+ */
+ private String[] getToken(String param1, String param2)
+ {
+ int i = 0;
+ String data[] = new String[8];
+ StringTokenizer tmp = new StringTokenizer(param1, param2);
+
+ while(tmp.hasMoreTokens())
+ {
+ data[i] = tmp.nextToken();
+ i++;
+ }
+ return data;
+ }
+
+ /**
+ * Returns a byte array containing an image (most often a photo or an
+ * avatar) that the contact uses as a representation.
+ *
+ * @return byte[] an image representing the contact.
+ */
+ public byte[] getImage()
+ {
+ return null;
+ }
+
+ /**
+ * Returns the status of the contact.
+ *
+ * @return RssStatusEnum.STATUS.
+ */
+ public PresenceStatus getPresenceStatus()
+ {
+ return this.presenceStatus;
+ }
+
+ /**
+ * Sets <tt>rssPresenceStatus</tt> as the PresenceStatus that this
+ * contact is currently in.
+ * @param rssPresenceStatus the <tt>RssPresenceStatus</tt>
+ * currently valid for this contact.
+ */
+ public void setPresenceStatus(PresenceStatus rssPresenceStatus)
+ {
+ this.presenceStatus = rssPresenceStatus;
+ }
+
+ /**
+ * Returns a reference to the protocol provider that created the contact.
+ *
+ * @return a refererence to an instance of the ProtocolProviderService
+ */
+ public ProtocolProviderService getProtocolProvider()
+ {
+ return parentProvider;
+ }
+
+ /**
+ * Determines whether or not this contact represents our own identity.
+ *
+ * @return true in case this is a contact that represents ourselves and
+ * false otherwise.
+ */
+ public boolean isLocal()
+ {
+ return false;
+ }
+
+ /**
+ * Returns the group that contains this contact.
+ * @return a reference to the <tt>ContactGroupRssImpl</tt> that
+ * contains this contact.
+ */
+ public ContactGroup getParentContactGroup()
+ {
+ return this.parentGroup;
+ }
+
+ /**
+ * 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("ContactRssImpl[ DisplayName=")
+ .append(getDisplayName()).append("]");
+
+ return buff.toString();
+ }
+
+ /**
+ * 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 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.
+ *
+ * @param isPersistent true if the contact is persistent and false
+ * otherwise.
+ */
+ public void setPersistent(boolean isPersistent)
+ {
+ this.isPersistent = isPersistent;
+ }
+
+ /**
+ * 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()
+ {
+ // to store data only when lastDate is set
+ if(lastDate != null)
+ return "lastDate=" + lastDate + ";";
+ else
+ return null;
+ }
+
+ public void setPersistentData(String persistentData)
+ {
+ if(persistentData == null)
+ {
+ return;
+ }
+
+ StringTokenizer dataToks = new StringTokenizer(persistentData, ";");
+ while(dataToks.hasMoreTokens())
+ {
+ String data[] = dataToks.nextToken().split("=");
+ if(data[0].equals("lastDate") && data.length > 1)
+ {
+ this.lastDate = data[1];
+ convertStringToDate(this.lastDate);
+ }
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Makes the contact resolved or unresolved.
+ *
+ * @param resolved true to make the contact resolved; false to
+ * make it unresolved
+ */
+ public void setResolved(boolean resolved)
+ {
+ this.isResolved = resolved;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one which in terms
+ * of contacts translates to having equal ids. The resolved status of the
+ * contacts deliberately ignored so that contacts would be declared equal
+ * even if it differs.
+ * <p>
+ * @param obj the reference object with which to compare.
+ * @return <code>true</code> if this contact has the same id as that of the
+ * <code>obj</code> argument.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == null
+ || ! (obj instanceof ContactRssImpl))
+ return false;
+
+ ContactRssImpl rssContact = (ContactRssImpl) obj;
+
+ return this.getAddress().equals(rssContact.getAddress());
+ }
+
+
+ /**
+ * Returns the persistent presence operation set that this contact belongs
+ * to.
+ *
+ * @return the <tt>OperationSetPersistentPresenceRssImpl</tt> that
+ * this contact belongs to.
+ */
+ public OperationSetPersistentPresenceRssImpl
+ getParentPresenceOperationSet()
+ {
+ return (OperationSetPersistentPresenceRssImpl)parentProvider
+ .getOperationSet(OperationSetPersistentPresence.class);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/MessageRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/MessageRssImpl.java
new file mode 100644
index 0000000..1071bdf
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/MessageRssImpl.java
@@ -0,0 +1,137 @@
+/*
+ * 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.rss;
+
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Very simple message implementation for the Rss protocol.
+ *
+ * @author Emil Ivov
+ */
+public class MessageRssImpl
+ implements Message
+{
+ /**
+ * The actual message content.
+ */
+ private String textContent = null;
+
+ /**
+ * The content type of the message. (text/plain if null)
+ */
+ private String contentType = null;
+
+ /**
+ * The message encoding. (UTF8 if null).
+ */
+ private String contentEncoding = null;
+
+ /**
+ * A String uniquely identifying the message
+ */
+ private String messageUID = null;
+
+ /**
+ * The subject of the message. (most often is null)
+ */
+ private String subject = null;
+
+ /**
+ * Creates a message instance according to the specified parameters.
+ *
+ * @param content the message body
+ * @param contentType message content type or null for text/plain
+ * @param contentEncoding message encoding or null for UTF8
+ * @param subject the subject of the message or null for no subject.
+ */
+ public MessageRssImpl(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 message body.
+ *
+ * @return the message content.
+ */
+ public String getContent()
+ {
+ return textContent;
+ }
+
+ /**
+ * Returns the type of the content of this message.
+ *
+ * @return the type of the content of this message.
+ */
+ public String getContentType()
+ {
+ return contentType;
+ }
+
+ /**
+ * Returns the encoding used for the message content.
+ *
+ * @return the encoding of the message body.
+ */
+ public String getEncoding()
+ {
+ return contentEncoding;
+ }
+
+ /**
+ * A string uniquely identifying the message.
+ *
+ * @return a <tt>String</tt> uniquely identifying the message.
+ */
+ public String getMessageUID()
+ {
+ return messageUID;
+ }
+
+ /**
+ * Returns the message body in a binary form.
+ *
+ * @return a <tt>byte[]</tt> representation of the message body.
+ */
+ public byte[] getRawData()
+ {
+ return getContent().getBytes();
+ }
+
+ /**
+ * Return the length of this message.
+ *
+ * @return the length of this message.
+ */
+ public int getSize()
+ {
+ return getContent().length();
+ }
+
+ /**
+ * Returns the message subject.
+ *
+ * @return the message subject.
+ */
+ public String getSubject()
+ {
+ return subject;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java
new file mode 100644
index 0000000..ef76a7a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java
@@ -0,0 +1,436 @@
+/*
+ * 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.rss;
+
+import java.util.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * Instant messaging functionalites for the Rss protocol.
+ *
+ * @author Jean-Albert Vescovo
+ */
+public class OperationSetBasicInstantMessagingRssImpl
+ implements OperationSetBasicInstantMessaging
+{
+ /**
+ * Currently registered message listeners.
+ */
+ private Vector messageListeners = new Vector();
+
+ /**
+ * The currently valid persistent presence operation set..
+ */
+ private OperationSetPersistentPresenceRssImpl opSetPersPresence = null;
+
+ /**
+ * The protocol provider that created us.
+ */
+ private ProtocolProviderServiceRssImpl parentProvider = null;
+
+ /**
+ * The timer used in order to refresh one or more rss feeds
+ */
+ private Timer timer = null;
+
+ /**
+ * The value corresponding to the time in ms
+ * of the rss refreshing period (here 10min)
+ */
+ final int PERIOD_REFRESH_RSS = 600000;
+
+ /**
+ * Creates an instance of this operation set keeping a reference to the
+ * parent protocol provider and presence operation set.
+ *
+ * @param provider The provider instance that creates us.
+ * @param opSetPersPresence the currently valid
+ * <tt>OperationSetPersistentPresenceRssImpl</tt> instance.
+ */
+ public OperationSetBasicInstantMessagingRssImpl(
+ ProtocolProviderServiceRssImpl provider,
+ OperationSetPersistentPresenceRssImpl opSetPersPresence)
+ {
+ this.opSetPersPresence = opSetPersPresence;
+ this.parentProvider = provider;
+ }
+
+ /**
+ * Registers 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)
+ {
+ if(!messageListeners.contains(listener))
+ messageListeners.add(listener);
+ }
+
+ /**
+ * 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 MessageRssImpl(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 MessageRssImpl(messageText, DEFAULT_MIME_TYPE
+ , DEFAULT_MIME_ENCODING, null);
+ }
+
+ /**
+ * 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)
+ {
+ messageListeners.remove(listener);
+ }
+
+ /**
+ * Looks for a RSS feed specified as contact
+ *
+ * @param rssContact the <tt>contact</tt> to send query
+ * @param newContact the <tt>boolean</tt> to now if it's a new feed/contact
+ * @param aloneUpdate the <tt>boolean</tt> to know if it's
+ * a query just for one feed/contact
+ */
+ private void submitRssQuery(ContactRssImpl rssContact,
+ boolean newContact,
+ boolean aloneUpdate)
+ {
+ Message msg;
+ boolean newName = false;
+ boolean newDate = false;
+ boolean update = false;
+ Date lastQueryDate = null;
+ String newDisplayName = new String();
+ String oldDisplayName = new String();
+
+ //we instantiate a new RssFeedReader which will contain the feed retrieved
+ RssFeedReader rssFeed = new RssFeedReader(rssContact.getAddress());
+
+ //we parse the feed/contact
+ rssFeed.recupFlux();
+
+ if(rssFeed.getFeed() == null)
+ {
+ msg = createMessage("No RSS feed available at URL "+ rssContact.getAddress());
+ }else
+ {
+ //we recover the feed's old name
+ if(newContact)
+ oldDisplayName = rssContact.getDisplayName();
+ else
+ oldDisplayName = rssFeed.getTitle();
+
+ //we change the contact's displayName according to the feed's title
+ newDisplayName = rssFeed.getTitle();
+ if(!(newDisplayName.equals(oldDisplayName)))
+ {
+ newName = true;
+ }
+ rssContact.setDisplayName(newDisplayName);
+
+ //Looking for a date representing the last item retrieved on this feed
+ //we look after a date saving in the contact's parameters (i.e. in the
+ // file contactlist.xml)
+ if(rssContact.getDate() != null)
+ lastQueryDate = rssContact.getDate();
+
+ //we create the message containing the new items retrieved
+ msg = createMessage(rssFeed.getPrintedFeed(lastQueryDate));
+
+ //if a newer date is avalaible for the current feed/contact looking the
+ // date of each item of the feed retrieved, we update this date
+ if(rssFeed.getUltimateItemDate() != null)
+ {
+ if(lastQueryDate != null)
+ {
+ if(rssFeed.getUltimateItemDate().compareTo(lastQueryDate)>0)
+ {
+ rssContact.setDate(rssFeed.getUltimateItemDate());
+ newDate = true;
+ update = true;
+ }
+ }
+ else
+ {
+ rssContact.setDate(rssFeed.getUltimateItemDate());
+ newDate = true;
+ update = true;
+ }
+ }
+ else
+ update = true;
+
+ //if we have a new date or a new name on this feed/contact, we fire that
+ // the contact has his properties modified in order to save it
+ if(newName || newDate)
+ this.opSetPersPresence.fireContactPropertyChangeEvent(
+ ContactPropertyChangeEvent.
+ PROPERTY_DISPLAY_NAME, rssContact,
+ oldDisplayName, newDisplayName);
+ }
+
+ //if the feed has been updated or if the user made a request on a specific
+ //feed/contact, we fire a new message containing the new items to the user
+ if(update || aloneUpdate)
+ fireMessageReceived(msg, rssContact);
+ }
+
+ /**
+ * To refresh all rss feeds registered as contacts
+ */
+ public void refreshRssFeed()
+ {
+ Vector rssContactList = new Vector();
+ rssContactList = opSetPersPresence.getContactListRoot().
+ getRssURLList(rssContactList);
+ Iterator rssContact = rssContactList.iterator();
+ while(rssContact.hasNext())
+ {
+ submitRssQuery((ContactRssImpl)rssContact.next(), false, false);
+ }
+ }
+
+ /**
+ * To refresh a specific rss feed specified as param
+ *
+ * @param rssURL the <tt>contact</tt> to be refreshed
+ * @param newContact
+ * @param aloneUpdate
+ */
+ public void refreshRssFeed( ContactRssImpl rssURL,
+ boolean newContact,
+ boolean aloneUpdate)
+ {
+ submitRssQuery(rssURL, newContact, aloneUpdate);
+ }
+
+ /**
+ * Creating the timer permitting the refresh of rss feeds
+ */
+ public void createTimer()
+ {
+ RssTimerRefreshFeed refresh = new RssTimerRefreshFeed(this);
+ this.timer = new Timer();
+ this.timer.scheduleAtFixedRate(refresh, 0, PERIOD_REFRESH_RSS);
+ }
+
+ /**
+ * Cancel the timer if the user switch to the OFFLINE status
+ */
+ public void stopTimer(){
+ this.timer.cancel();
+ }
+
+ /**
+ * Retrieve the feeds for a new Rss Feed just added as persistent contact
+ *
+ * @param contact the <tt>Contact</tt> added
+ */
+ public void newContact(ContactRssImpl contact)
+ {
+ RssThread rssThr = new RssThread(this,contact, true, true);
+ }
+
+ /**
+ * 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 IllegalStateException if the underlying ICQ stack is not
+ * registered and initialized.
+ * @throws IllegalArgumentException if <tt>to</tt> is not an instance
+ * belonging to the underlying implementation.
+ */
+ public void sendInstantMessage(Contact to, Message message)
+ throws IllegalStateException,
+ IllegalArgumentException
+ {
+ if( !(to instanceof ContactRssImpl) )
+ throw new IllegalArgumentException(
+ "The specified contact is not a Rss contact."
+ + to);
+
+ MessageDeliveredEvent msgDeliveredEvt
+ = new MessageDeliveredEvent(message, to, new Date());
+
+ //refresh the present rssFeed "to"
+ fireMessageDelivered(message,to);
+ RssThread rssThr = new RssThread(this, (ContactRssImpl)to, false, true);
+ }
+
+ /**
+ * In case the to the <tt>to</tt> Contact corresponds to another rss
+ * protocol provider registered with SIP Communicator, we deliver
+ * the message to them, in case the <tt>to</tt> Contact represents us, we
+ * fire a <tt>MessageReceivedEvent</tt>, and if <tt>to</tt> is simply
+ * a contact in our contact list, then we simply echo the message.
+ *
+ * @param message the <tt>Message</tt> the message to deliver.
+ * @param to the <tt>Contact</tt> that we should deliver the message to.
+ */
+ private void deliverMessage(Message message, ContactRssImpl to)
+ {
+ String userID = to.getAddress();
+
+ //if the user id is owr own id, then this message is being routed to us
+ //from another instance of the rss provider.
+ if (userID.equals(this.parentProvider.getAccountID().getUserID()))
+ {
+ //check who is the provider sending the message
+ String sourceUserID
+ = to.getProtocolProvider().getAccountID().getUserID();
+
+ //check whether they are in our contact list
+ Contact from = opSetPersPresence.findContactByID(sourceUserID);
+
+
+ //and if not - add them there as volatile.
+ if(from == null)
+ {
+ from = opSetPersPresence.createVolatileContact(sourceUserID);
+ }
+
+ //and now fire the message received event.
+ fireMessageReceived(message, from);
+ }
+ else
+ {
+ //if userID is not our own, try an check whether another provider
+ //has that id and if yes - deliver the message to them.
+ ProtocolProviderServiceRssImpl rssProvider
+ = this.opSetPersPresence.findProviderForRssUserID(userID);
+ if(rssProvider != null)
+ {
+ OperationSetBasicInstantMessagingRssImpl opSetIM
+ = (OperationSetBasicInstantMessagingRssImpl)
+ rssProvider.getOperationSet(
+ OperationSetBasicInstantMessaging.class);
+ opSetIM.deliverMessage(message, to);
+ }
+ else
+ {
+ //if we got here then "to" is simply someone in our contact
+ //list so let's just echo the message.
+ fireMessageReceived(message, to);
+ }
+ }
+ }
+
+ /**
+ * Notifies all registered message listeners that a message has been
+ * delivered successfully to its addressee..
+ *
+ * @param message the <tt>Message</tt> that has been delivered.
+ * @param to the <tt>Contact</tt> that <tt>message</tt> was delivered to.
+ */
+ private void fireMessageDelivered(Message message, Contact to)
+ {
+ MessageDeliveredEvent evt
+ = new MessageDeliveredEvent(message, to, new Date());
+
+ Iterator listeners = null;
+ synchronized (messageListeners)
+ {
+ listeners = new ArrayList(messageListeners).iterator();
+ }
+
+ while (listeners.hasNext())
+ {
+ MessageListener listener
+ = (MessageListener) listeners.next();
+
+ listener.messageDelivered(evt);
+ }
+ }
+
+ /**
+ * Notifies all registered message listeners that a message has been
+ * received.
+ *
+ * @param message the <tt>Message</tt> that has been received.
+ * @param from the <tt>Contact</tt> that <tt>message</tt> was received from.
+ */
+ private void fireMessageReceived(Message message, Contact from)
+ {
+ MessageReceivedEvent evt
+ = new MessageReceivedEvent(message, from, new Date());
+
+ Iterator listeners = null;
+ synchronized (messageListeners)
+ {
+ listeners = new ArrayList(messageListeners).iterator();
+ }
+
+ while (listeners.hasNext())
+ {
+ MessageListener listener
+ = (MessageListener) listeners.next();
+
+ listener.messageReceived(evt);
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ public ProtocolProviderServiceRssImpl getParentProvider(){
+ return this.parentProvider;
+ }
+
+ public OperationSetPersistentPresenceRssImpl getOpSetPersPresence(){
+ return this.opSetPersPresence;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/OperationSetPersistentPresenceRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/OperationSetPersistentPresenceRssImpl.java
new file mode 100644
index 0000000..e9ab143
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/OperationSetPersistentPresenceRssImpl.java
@@ -0,0 +1,1357 @@
+/*
+ * 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.rss;
+
+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 org.osgi.framework.*;
+
+/**
+ * A Rss implementation of a persistent presence operation set. In order
+ * to simulate server persistence, this operation set would simply accept all
+ * unresolved contacts and resolve them immediately. A real world protocol
+ * implementation would save it on a server using methods provided by the
+ * protocol stack.
+ *
+ * @author Emil Ivov/Jean-Albert Vescovo
+ */
+public class OperationSetPersistentPresenceRssImpl
+ implements OperationSetPersistentPresence
+{
+ private static final Logger logger =
+ Logger.getLogger(OperationSetPersistentPresenceRssImpl.class);
+ /**
+ * A list of listeners registered for <tt>SubscriptionEvent</tt>s.
+ */
+ private Vector subscriptionListeners = new Vector();
+
+ /**
+ * A list of listeners registered for <tt>ServerStoredGroupChangeEvent</tt>s.
+ */
+ private Vector serverStoredGroupListeners = new Vector();
+
+ /**
+ * A list of listeners registered for
+ * <tt>ProviderPresenceStatusChangeEvent</tt>s.
+ */
+ private Vector providerPresenceStatusListeners = new Vector();
+
+ /**
+ * A list of listeneres registered for
+ * <tt>ContactPresenceStatusChangeEvent</tt>s.
+ */
+ private Vector contactPresenceStatusListeners = new Vector();
+
+ /**
+ * The root of the rss contact list.
+ */
+ private ContactGroupRssImpl contactListRoot = null;
+
+ /**
+ * The provider that created us.
+ */
+ private ProtocolProviderServiceRssImpl parentProvider = null;
+
+ /**
+ * The currently active status message.
+ */
+ private String statusMessage = "Default Status Message";
+
+ /**
+ * Our default presence status.
+ */
+ private PresenceStatus presenceStatus = RssStatusEnum.ONLINE;
+
+ /**
+ * The <tt>AuthorizationHandler</tt> instance that we'd have to transmit
+ * authorization requests to for approval.
+ */
+ private AuthorizationHandler authorizationHandler = null;
+
+ /**
+ * Creates an instance of this operation set keeping a reference to the
+ * specified parent <tt>provider</tt>.
+ * @param provider the ProtocolProviderServiceRssImpl instance that
+ * created us.
+ */
+ public OperationSetPersistentPresenceRssImpl(
+ ProtocolProviderServiceRssImpl provider)
+ {
+ this.parentProvider = provider;
+ contactListRoot = new ContactGroupRssImpl("RootGroup", provider);
+
+ //add our unregistration listener
+ parentProvider.addRegistrationStateChangeListener(
+ new UnregistrationListener());
+ }
+
+ /**
+ * Rss implementation of the corresponding ProtocolProviderService
+ * method.
+ *
+ * @param listener a dummy param.
+ */
+ public void addContactPresenceStatusListener(
+ ContactPresenceStatusListener listener)
+ {
+ synchronized(contactPresenceStatusListeners)
+ {
+ if (!contactPresenceStatusListeners.contains(listener))
+ contactPresenceStatusListeners.add(listener);
+ }
+ }
+
+ /**
+ * Notifies all registered listeners of the new event.
+ *
+ * @param source the contact that has caused the event.
+ * @param parentGroup the group that contains the source contact.
+ * @param oldValue the status that the source contact detained before
+ * changing it.
+ */
+ public void fireContactPresenceStatusChangeEvent(ContactRssImpl source,
+ ContactGroup parentGroup,
+ PresenceStatus oldValue)
+ {
+ ContactPresenceStatusChangeEvent evt
+ = new ContactPresenceStatusChangeEvent(source, parentProvider
+ , parentGroup, oldValue, source.getPresenceStatus());
+
+ Iterator listeners = null;
+ synchronized(contactPresenceStatusListeners)
+ {
+ listeners = new ArrayList(contactPresenceStatusListeners).iterator();
+ }
+
+
+ while(listeners.hasNext())
+ {
+ ContactPresenceStatusListener listener
+ = (ContactPresenceStatusListener)listeners.next();
+
+ listener.contactPresenceStatusChanged(evt);
+ }
+ }
+
+ /**
+ * Notify all subscription listeners of the corresponding contact property
+ * change event.
+ *
+ * @param eventID the String ID of the event to dispatch
+ * @param sourceContact the ContactRssImpl instance that this event is
+ * pertaining to.
+ * @param oldValue the value that the changed property had before the change
+ * occurred.
+ * @param newValue the value that the changed property currently has (after
+ * the change has occurred).
+ */
+ void fireContactPropertyChangeEvent( String eventID,
+ ContactRssImpl sourceContact,
+ Object oldValue,
+ Object newValue)
+ {
+ ContactPropertyChangeEvent evt =
+ new ContactPropertyChangeEvent(sourceContact, eventID
+ , oldValue, newValue);
+
+ logger.debug("Dispatching a Contact Property Change 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.contactModified(evt);
+ }
+ }
+
+ /**
+ * Notifies all registered listeners of the new event.
+ *
+ * @param source the contact that has caused the event.
+ * @param parentGroup the group that contains the source contact.
+ * @param eventID an identifier of the event to dispatch.
+ */
+ public void fireSubscriptionEvent(ContactRssImpl source,
+ ContactGroup parentGroup,
+ int eventID)
+ {
+ SubscriptionEvent evt = new SubscriptionEvent(source
+ , this.parentProvider
+ , parentGroup
+ , eventID);
+
+ Iterator listeners = null;
+ synchronized (subscriptionListeners)
+ {
+ listeners = new ArrayList(subscriptionListeners).iterator();
+ }
+
+ while (listeners.hasNext())
+ {
+ SubscriptionListener listener
+ = (SubscriptionListener) listeners.next();
+
+ if(eventID == SubscriptionEvent.SUBSCRIPTION_CREATED)
+ {
+ listener.subscriptionCreated(evt);
+ }
+ else if (eventID == SubscriptionEvent.SUBSCRIPTION_FAILED)
+ {
+ listener.subscriptionFailed(evt);
+ }
+ else if (eventID == SubscriptionEvent.SUBSCRIPTION_REMOVED)
+ {
+ listener.subscriptionRemoved(evt);
+ }
+ }
+ }
+
+ /**
+ * Notifies all registered listeners of the new event.
+ *
+ * @param source the contact that has been moved..
+ * @param oldParent the group where the contact was located before being
+ * moved.
+ * @param newParent the group where the contact has been moved.
+ */
+ public void fireSubscriptionMovedEvent(Contact source,
+ ContactGroup oldParent,
+ ContactGroup newParent)
+ {
+ SubscriptionMovedEvent evt = new SubscriptionMovedEvent(source
+ , this.parentProvider
+ , oldParent
+ , newParent);
+
+ Iterator listeners = null;
+ synchronized (subscriptionListeners)
+ {
+ listeners = new ArrayList(subscriptionListeners).iterator();
+ }
+
+ while (listeners.hasNext())
+ {
+ SubscriptionListener listener
+ = (SubscriptionListener) listeners.next();
+
+ listener.subscriptionMoved(evt);
+ }
+ }
+
+
+ /**
+ * Notifies all registered listeners of the new event.
+ *
+ * @param source the contact that has caused the event.
+ * @param eventID an identifier of the event to dispatch.
+ */
+ public void fireServerStoredGroupEvent(ContactGroupRssImpl source,
+ int eventID)
+ {
+ ServerStoredGroupEvent evt = new ServerStoredGroupEvent(
+ source, eventID, (ContactGroupRssImpl)source.getParentContactGroup()
+ , this.parentProvider, this);
+
+ Iterator listeners = null;
+ synchronized (serverStoredGroupListeners)
+ {
+ listeners = new ArrayList(serverStoredGroupListeners).iterator();
+ }
+
+ while (listeners.hasNext())
+ {
+ ServerStoredGroupListener listener
+ = (ServerStoredGroupListener) listeners.next();
+
+ if(eventID == ServerStoredGroupEvent.GROUP_CREATED_EVENT)
+ {
+ listener.groupCreated(evt);
+ }
+ else if(eventID == ServerStoredGroupEvent.GROUP_RENAMED_EVENT)
+ {
+ listener.groupNameChanged(evt);
+ }
+ else if(eventID == ServerStoredGroupEvent.GROUP_REMOVED_EVENT)
+ {
+ listener.groupRemoved(evt);
+ }
+ }
+ }
+
+ /**
+ * Notifies all registered listeners of the new event.
+ *
+ * @param oldValue the presence status we were in before the change.
+ */
+ public void fireProviderStatusChangeEvent(PresenceStatus oldValue)
+ {
+ ProviderPresenceStatusChangeEvent evt
+ = new ProviderPresenceStatusChangeEvent(this.parentProvider,
+ oldValue, this.getPresenceStatus());
+
+ Iterator listeners = null;
+ synchronized (providerPresenceStatusListeners)
+ {
+ listeners = new ArrayList(providerPresenceStatusListeners).iterator();
+ }
+
+ while (listeners.hasNext())
+ {
+ ProviderPresenceStatusListener listener
+ = (ProviderPresenceStatusListener) listeners.next();
+
+ //listener.providerStatusChanged(evt);
+ }
+ }
+
+ /**
+ * Rss implementation of the corresponding ProtocolProviderService
+ * method.
+ *
+ * @param listener a dummy param.
+ */
+ public void addProviderPresenceStatusListener(
+ ProviderPresenceStatusListener listener)
+ {
+ synchronized(providerPresenceStatusListeners)
+ {
+ if (!providerPresenceStatusListeners.contains(listener))
+ this.providerPresenceStatusListeners.add(listener);
+ }
+ }
+
+ /**
+ * Registers a listener that would receive events upon changes in server
+ * stored groups.
+ *
+ * @param listener a ServerStoredGroupChangeListener impl that would
+ * receive events upong group changes.
+ */
+ public void addServerStoredGroupChangeListener(ServerStoredGroupListener
+ listener)
+ {
+ synchronized(serverStoredGroupListeners)
+ {
+ if (!serverStoredGroupListeners.contains(listener))
+ serverStoredGroupListeners.add(listener);
+ }
+ }
+
+ /**
+ * Rss implementation of the corresponding ProtocolProviderService
+ * method.
+ *
+ * @param listener the SubscriptionListener to register
+ */
+ public void addSubsciptionListener(SubscriptionListener listener)
+ {
+ synchronized(subscriptionListeners)
+ {
+ if (!subscriptionListeners.contains(listener))
+ this.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.
+ */
+ public void createServerStoredContactGroup(ContactGroup parent,
+ String groupName)
+ {
+ ContactGroupRssImpl newGroup
+ = new ContactGroupRssImpl(groupName, parentProvider);
+
+ ((ContactGroupRssImpl)parent).addSubgroup(newGroup);
+
+ this.fireServerStoredGroupEvent(
+ newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT);
+ }
+
+ /**
+ * A Rss Provider method to use for fast filling of a contact list.
+ *
+ * @param contactGroup the group to add
+ */
+ public void addRssGroup(ContactGroupRssImpl contactGroup)
+ {
+ contactListRoot.addSubgroup(contactGroup);
+ }
+
+ /**
+ * A Rss Provider method to use for fast filling of a contact list.
+ * This method would add both the group and fire an event.
+ *
+ * @param parent the group where <tt>contactGroup</tt> should be added.
+ * @param contactGroup the group to add
+ */
+ public void addRssGroupAndFireEvent(
+ ContactGroupRssImpl parent
+ , ContactGroupRssImpl contactGroup)
+ {
+ parent.addSubgroup(contactGroup);
+
+ this.fireServerStoredGroupEvent(
+ contactGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT);
+ }
+
+
+ /**
+ * 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 contactListRoot.findContactByID(contactID);
+ }
+
+ /**
+ * Sets the specified status message.
+ * @param statusMessage a String containing the new status message.
+ */
+ public void setStatusMessage(String statusMessage)
+ {
+ this.statusMessage = statusMessage;
+ }
+
+ /**
+ * Returns the status message that was last set through
+ * setCurrentStatusMessage.
+ *
+ * @return the last status message that we have requested and the aim
+ * server has confirmed.
+ */
+ public String getCurrentStatusMessage()
+ {
+ return statusMessage;
+ }
+
+ /**
+ * 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 presenceStatus;
+ }
+
+ /**
+ * 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 contactListRoot;
+ }
+
+ /**
+ * 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 RssStatusEnum.supportedStatusSet();
+ }
+
+ public ContactGroupRssImpl getContactListRoot(){
+ return this.contactListRoot;
+ }
+
+ /**
+ * 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)
+ {
+ ContactRssImpl rssContact
+ = (ContactRssImpl)contactToMove;
+
+ ContactGroupRssImpl parentRssGroup
+ = findContactParent(rssContact);
+
+ parentRssGroup.removeContact(rssContact);
+
+ //if this is a volatile contact then we haven't really subscribed to
+ //them so we'd need to do so here
+ if(!rssContact.isPersistent())
+ {
+ //first tell everyone that the volatile contact was removed
+ fireSubscriptionEvent(rssContact
+ , parentRssGroup
+ , SubscriptionEvent.SUBSCRIPTION_REMOVED);
+
+ try
+ {
+ //now subscribe
+ this.subscribe(newParent, contactToMove.getAddress());
+
+ //now tell everyone that we've added the contact
+ fireSubscriptionEvent(rssContact
+ , newParent
+ , SubscriptionEvent.SUBSCRIPTION_CREATED);
+ }
+ catch (Exception ex)
+ {
+ logger.error("Failed to move contact "
+ + rssContact.getAddress()
+ , ex);
+ }
+ }
+ else
+ {
+ ( (ContactGroupRssImpl) newParent)
+ .addContact(rssContact);
+
+ fireSubscriptionMovedEvent(contactToMove
+ , parentRssGroup
+ , 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
+ {
+ PresenceStatus oldPresenceStatus = this.presenceStatus;
+ this.presenceStatus = status;
+ this.statusMessage = statusMessage;
+
+ this.fireProviderStatusChangeEvent(oldPresenceStatus);
+
+ //since we are not a real protocol, we set the contact presence status
+ //ourselves and make them have the same status as ours.
+ changePresenceStatusForAllContacts( getServerStoredContactListRoot()
+ , getPresenceStatus());
+
+ //now check whether we are in someone else's contact list and modify
+ //our status there
+ List contacts = findContactsPointingToUs();
+
+ Iterator contactsIter = contacts.iterator();
+ while (contactsIter.hasNext())
+ {
+ ContactRssImpl contact
+ = (ContactRssImpl) contactsIter.next();
+
+ PresenceStatus oldStatus = contact.getPresenceStatus();
+ contact.setPresenceStatus(status);
+ contact.getParentPresenceOperationSet()
+ .fireContactPresenceStatusChangeEvent(
+ contact
+ , contact.getParentContactGroup()
+ , oldStatus);
+
+ }
+ }
+
+
+
+ /**
+ * 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
+ {
+ return findContactByID(contactIdentifier).getPresenceStatus();
+ }
+
+ /**
+ * Sets the presence status of <tt>contact</tt> to <tt>newStatus</tt>.
+ *
+ * @param contact the <tt>ContactRssImpl</tt> whose status we'd like
+ * to set.
+ * @param newStatus the new status we'd like to set to <tt>contact</tt>.
+ */
+ private void changePresenceStatusForContact(
+ ContactRssImpl contact
+ , PresenceStatus newStatus)
+ {
+ PresenceStatus oldStatus = contact.getPresenceStatus();
+ contact.setPresenceStatus(newStatus);
+
+ fireContactPresenceStatusChangeEvent(
+ contact, findContactParent(contact), oldStatus);
+ }
+
+ /**
+ * Sets the presence status of all <tt>contact</tt>s in our contact list
+ * (except those that correspond to another provider registered with SC)
+ * to <tt>newStatus</tt>.
+ *
+ * @param newStatus the new status we'd like to set to <tt>contact</tt>.
+ * @param parent the group in which we'd have to update the status of all
+ * direct and indirect child contacts.
+ */
+ private void changePresenceStatusForAllContacts(ContactGroup parent,
+ PresenceStatus newStatus)
+ {
+ //first set the status for contacts in this group
+ Iterator childContacts = parent.contacts();
+
+ while(childContacts.hasNext())
+ {
+ ContactRssImpl contact
+ = (ContactRssImpl)childContacts.next();
+
+ if(findProviderForRssUserID(contact.getAddress()) != null)
+ {
+ //this is a contact corresponding to another SIP Communicator
+ //provider so we won't change it's status here.
+ continue;
+ }
+ PresenceStatus oldStatus = contact.getPresenceStatus();
+ contact.setPresenceStatus(newStatus);
+
+ fireContactPresenceStatusChangeEvent(
+ contact, parent, oldStatus);
+ }
+
+ //now call this method recursively for all subgroups
+ Iterator subgroups = parent.subgroups();
+
+ while(subgroups.hasNext())
+ {
+ ContactGroup subgroup = (ContactGroup)subgroups.next();
+ changePresenceStatusForAllContacts(subgroup, newStatus);
+ }
+ }
+
+
+ /**
+ * 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)
+ {
+ this.providerPresenceStatusListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Returns the group that is parent of the specified rssGroup or null
+ * if no parent was found.
+ * @param rssGroup the group whose parent we're looking for.
+ * @return the ContactGroupRssImpl instance that rssGroup
+ * belongs to or null if no parent was found.
+ */
+ public ContactGroupRssImpl findGroupParent(ContactGroupRssImpl rssGroup)
+ {
+ return contactListRoot.findGroupParent(rssGroup);
+ }
+
+ /**
+ * Returns the group that is parent of the specified rssContact or
+ * null if no parent was found.
+ * @param rssContact the contact whose parent we're looking for.
+ * @return the ContactGroupRssImpl instance that rssContact
+ * belongs to or null if no parent was found.
+ */
+ public ContactGroupRssImpl findContactParent(
+ ContactRssImpl rssContact)
+ {
+ return (ContactGroupRssImpl)rssContact.getParentContactGroup();
+ }
+
+ /**
+ * Removes the specified group from the server stored contact list.
+ *
+ * @param group the group to remove.
+ *
+ * @throws IllegalArgumentException if <tt>group</tt> was not found in this
+ * protocol's contact list.
+ */
+ public void removeServerStoredContactGroup(ContactGroup group)
+ throws IllegalArgumentException
+ {
+ ContactGroupRssImpl rssGroup = (ContactGroupRssImpl)group;
+
+ ContactGroupRssImpl parent = findGroupParent(rssGroup);
+
+ if(parent == null){
+ throw new IllegalArgumentException(
+ "group " + group
+ + " does not seem to belong to this protocol's contact list.");
+ }
+
+ parent.removeSubGroup(rssGroup);
+
+ this.fireServerStoredGroupEvent(
+ rssGroup, ServerStoredGroupEvent.GROUP_REMOVED_EVENT);
+ }
+
+
+ /**
+ * 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)
+ {
+ synchronized(serverStoredGroupListeners)
+ {
+ serverStoredGroupListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Removes the specified subscription listener.
+ *
+ * @param listener the listener to remove.
+ */
+ public void removeSubscriptionListener(SubscriptionListener listener)
+ {
+ synchronized(subscriptionListeners)
+ {
+ this.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)
+ {
+ ((ContactGroupRssImpl)group).setGroupName(newName);
+
+ this.fireServerStoredGroupEvent(
+ (ContactGroupRssImpl)group,
+ ServerStoredGroupEvent.GROUP_RENAMED_EVENT);
+ }
+
+ /**
+ * 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.authorizationHandler = 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
+ {
+ ContactRssImpl contact = new ContactRssImpl(
+ contactIdentifier
+ , parentProvider);
+
+ ((ContactGroupRssImpl)parent).addContact(contact);
+
+
+ fireSubscriptionEvent(contact,
+ parent,
+ SubscriptionEvent.SUBSCRIPTION_CREATED);
+
+ //if the newly added contact corresponds to another provider - set their
+ //status accordingly
+ ProtocolProviderServiceRssImpl rssProvider
+ = findProviderForRssUserID(contactIdentifier);
+ if(rssProvider != null)
+ {
+ OperationSetPersistentPresence opSetPresence
+ = (OperationSetPersistentPresence)rssProvider.getOperationSet(
+ OperationSetPersistentPresence.class);
+
+ changePresenceStatusForContact(
+ contact
+ , (RssStatusEnum)opSetPresence.getPresenceStatus());
+ }
+ else
+ {
+ //otherwise - since we are not a real protocol, we set the contact
+ //presence status ourselves
+ changePresenceStatusForContact(contact, getPresenceStatus());
+ }
+ //just after inscription of the new contact, looking for the feed
+ this.parentProvider.getBasicInstantMessaging().newContact(contact);
+ }
+
+ /**
+ * Depending on whether <tt>contact</tt> corresponds to another protocol
+ * provider installed in sip-communicator, this method would either deliver
+ * it to that provider or simulate a corresponding request from the
+ * destination contact and make return a response after it has received
+ * one If the destination contact matches us, then we'll ask the user to
+ * act upon the request, and return the response.
+ *
+ * @param request the authorization request that we'd like to deliver to the
+ * desination <tt>contact</tt>.
+ * @param contact the <tt>Contact</tt> to notify
+ *
+ * @return the <tt>AuthorizationResponse</tt> that has been given or
+ * generated in response to <tt>request</tt>.
+ */
+ private AuthorizationResponse deliverAuthorizationRequest(
+ AuthorizationRequest request,
+ Contact contact)
+ {
+ String userID = contact.getAddress();
+
+ //if the user id is our own id, then this request is being routed to us
+ //from another instance of the rss provider.
+ if (userID.equals(this.parentProvider.getAccountID().getUserID()))
+ {
+ //check who is the provider sending the message
+ String sourceUserID = contact.getProtocolProvider()
+ .getAccountID().getUserID();
+
+ //check whether they are in our contact list
+ Contact from = findContactByID(sourceUserID);
+
+ //and if not - add them there as volatile.
+ if (from == null)
+ {
+ from = createVolatileContact(sourceUserID);
+ }
+
+ //and now handle the request.
+ return authorizationHandler.processAuthorisationRequest(
+ request, from);
+ }
+ else
+ {
+ //if userID is not our own, try a check whether another provider
+ //has that id and if yes - deliver the request to them.
+ ProtocolProviderServiceRssImpl rssProvider
+ = this.findProviderForRssUserID(userID);
+ if (rssProvider != null)
+ {
+ OperationSetPersistentPresenceRssImpl opSetPersPresence
+ = (OperationSetPersistentPresenceRssImpl)
+ rssProvider.getOperationSet(
+ OperationSetPersistentPresence.class);
+ return opSetPersPresence
+ .deliverAuthorizationRequest(request, contact);
+ }
+ else
+ {
+ //if we got here then "to" is simply someone in our contact
+ //list so let's just simulate a reciproce request and generate
+ //a response accordingly.
+
+ //pretend that the remote contact is asking for authorization
+ authorizationHandler.processAuthorisationRequest(
+ request, contact);
+
+ //and now pretend that the remote contact has granted us
+ //authorization
+ return new AuthorizationResponse(AuthorizationResponse.ACCEPT
+ , "You are welcome!");
+ }
+ }
+ }
+
+ /**
+ * 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
+ {
+ subscribe(contactListRoot, 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
+ {
+ ContactGroupRssImpl parentGroup
+ = (ContactGroupRssImpl)((ContactRssImpl)contact)
+ .getParentContactGroup();
+
+ parentGroup.removeContact((ContactRssImpl)contact);
+
+ fireSubscriptionEvent((ContactRssImpl)contact,
+ ((ContactRssImpl)contact).getParentContactGroup(),
+ SubscriptionEvent.SUBSCRIPTION_REMOVED);
+ }
+
+ /**
+ * Creates and returns a unresolved contact from the specified
+ * <tt>address</tt> and <tt>persistentData</tt>. The method will not try
+ * to establish a network connection and resolve the newly created Contact
+ * against the server. The protocol provider may will later try and resolve
+ * the contact. When this happens the corresponding event would notify
+ * interested subscription listeners.
+ *
+ * @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 from the specified
+ * <tt>address</tt> and <tt>persistentData</tt>. The method will not try
+ * to establish a network connection and resolve the newly created Contact
+ * against the server. The protocol provider may will later try and resolve
+ * the contact. When this happens the corresponding event would notify
+ * interested subscription listeners.
+ *
+ * @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 parent 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 parent)
+ {
+ ContactRssImpl contact = new ContactRssImpl(
+ address
+ , parentProvider);
+ contact.setResolved(false);
+
+ ( (ContactGroupRssImpl) parent).addContact(contact);
+
+ fireSubscriptionEvent(contact,
+ parent,
+ SubscriptionEvent.SUBSCRIPTION_CREATED);
+
+ //since we don't have any server, we'll simply resolve the contact
+ //ourselves as if we've just received an event from the server telling
+ //us that it has been resolved.
+ fireSubscriptionEvent(
+ contact, parent, SubscriptionEvent.SUBSCRIPTION_RESOLVED);
+
+ //since we are not a real protocol, we set the contact presence status
+ //ourselves
+ changePresenceStatusForContact( contact, getPresenceStatus());
+
+ //we retrieve if exists the persistent data for this contact
+ //which represents the date of the last item seen by the user
+ contact.setPersistentData(persistentData);
+
+ return contact;
+ }
+
+ /**
+ * Looks for a rss protocol provider registered for a user id matching
+ * <tt>rssUserID</tt>.
+ *
+ * @param rssUserID the ID of the Rss user whose corresponding
+ * protocol provider we'd like to find.
+ * @return ProtocolProviderServiceRssImpl a rss protocol
+ * provider registered for a user with id <tt>rssUserID</tt> or null
+ * if there is no such protocol provider.
+ */
+ public ProtocolProviderServiceRssImpl
+ findProviderForRssUserID(String rssUserID)
+ {
+ BundleContext bc = RssActivator.getBundleContext();
+
+ String osgiQuery = "(&"+
+ "(" + ProtocolProviderFactory.PROTOCOL + "=Rss)" +
+ "(" + ProtocolProviderFactory.USER_ID +
+ "=" + rssUserID + "))";
+
+ ServiceReference[] refs = null;
+ try
+ {
+ refs = bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ osgiQuery);
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ logger.error("Failed to execute the following osgi query: "
+ + osgiQuery
+ , ex);
+ }
+
+ if(refs != null && refs.length > 0)
+ {
+ return (ProtocolProviderServiceRssImpl)bc.getService(refs[0]);
+ }
+
+ return null;
+ }
+
+ /**
+ * Looks for rss protocol providers that have added us to their
+ * contact list and returns list of all contacts representing us in these
+ * providers.
+ *
+ * @return a list of all contacts in other providers' contact lists that
+ * point to us.
+ */
+ public List findContactsPointingToUs()
+ {
+ List contacts = new LinkedList();
+ BundleContext bc = RssActivator.getBundleContext();
+
+ String osgiQuery =
+ "(" + ProtocolProviderFactory.PROTOCOL
+ + "=Rss)";
+
+ ServiceReference[] refs = null;
+ try
+ {
+ refs = bc.getServiceReferences(
+ ProtocolProviderService.class.getName(), osgiQuery);
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ logger.error("Failed to execute the following osgi query: " +
+ osgiQuery, ex);
+ }
+
+ for (int i =0; refs != null && i < refs.length; i++)
+ {
+ ProtocolProviderServiceRssImpl gibProvider
+ = (ProtocolProviderServiceRssImpl)bc.getService(refs[i]);
+
+ OperationSetPersistentPresenceRssImpl opSetPersPresence
+ = (OperationSetPersistentPresenceRssImpl)gibProvider
+ .getOperationSet(OperationSetPersistentPresence.class);
+
+ Contact contact = opSetPersPresence.findContactByID(
+ parentProvider.getAccountID().getUserID());
+
+ if (contact != null)
+ contacts.add(contact);
+ }
+
+ return contacts;
+ }
+
+
+ /**
+ * Creates and returns a unresolved contact group from the specified
+ * <tt>address</tt> and <tt>persistentData</tt>. The method will not try
+ * to establish a network connection and resolve the newly created
+ * <tt>ContactGroup</tt> against the server or the contact itself. The
+ * protocol provider will later resolve the contact group. When this happens
+ * the corresponding event would notify interested subscription listeners.
+ *
+ * @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)
+ {
+ ContactGroupRssImpl newGroup
+ = new ContactGroupRssImpl(
+ ContactGroupRssImpl.createNameFromUID(groupUID)
+ , parentProvider);
+ newGroup.setResolved(false);
+
+ //if parent is null then we're adding under root.
+ if(parentGroup == null)
+ parentGroup = getServerStoredContactListRoot();
+
+ ((ContactGroupRssImpl)parentGroup).addSubgroup(newGroup);
+
+ this.fireServerStoredGroupEvent(
+ newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT);
+
+ return newGroup;
+ }
+
+ private class UnregistrationListener
+ implements RegistrationStateChangeListener
+ {
+ /**
+ * The method is called by a ProtocolProvider implementation whenver
+ * a change in the registration state of the corresponding provider had
+ * occurred. The method is particularly interested in events stating
+ * that the rss provider has unregistered so that it would fire
+ * status change events for all contacts in our buddy list.
+ *
+ * @param evt ProviderStatusChangeEvent the event describing the status
+ * change.
+ */
+ public void registrationStateChanged(RegistrationStateChangeEvent evt)
+ {
+ if (! evt.getNewState().equals(RegistrationState.UNREGISTERED)
+ && !evt.getNewState().equals(RegistrationState.AUTHENTICATION_FAILED)
+ && !evt.getNewState().equals(RegistrationState.CONNECTION_FAILED))
+ {
+ return;
+ }
+
+ //send event notifications saying that all our buddies are
+ //offline. The icq 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())
+ {
+ ContactGroupRssImpl group
+ = (ContactGroupRssImpl) groupsIter.next();
+
+ Iterator contactsIter = group.contacts();
+
+ while (contactsIter.hasNext())
+ {
+ ContactRssImpl contact
+ = (ContactRssImpl) contactsIter.next();
+
+ PresenceStatus oldContactStatus
+ = contact.getPresenceStatus();
+
+ if (!oldContactStatus.isOnline())
+ continue;
+
+ contact.setPresenceStatus(RssStatusEnum.ONLINE);
+
+ fireContactPresenceStatusChangeEvent(
+ contact
+ , contact.getParentContactGroup()
+ , oldContactStatus);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the volatile group or null if this group has not yet been
+ * created.
+ *
+ * @return a volatile group existing in our contact list or <tt>null</tt>
+ * if such a group has not yet been created.
+ */
+ private ContactGroupRssImpl getNonPersistentGroup()
+ {
+ for (int i = 0
+ ; i < getServerStoredContactListRoot().countSubgroups()
+ ; i++)
+ {
+ ContactGroupRssImpl gr =
+ (ContactGroupRssImpl)getServerStoredContactListRoot()
+ .getGroup(i);
+
+ if(!gr.isPersistent())
+ return gr;
+ }
+
+ return null;
+ }
+
+
+ /**
+ * 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 contactAddress the address of the volatile contact we'd like to
+ * create.
+ * @return the newly created volatile contact.
+ */
+ public ContactRssImpl createVolatileContact(String contactAddress)
+ {
+ //First create the new volatile contact;
+ ContactRssImpl newVolatileContact
+ = new ContactRssImpl(contactAddress
+ , this.parentProvider);
+ newVolatileContact.setPersistent(false);
+
+
+ //Check whether a volatile group already exists and if not create
+ //one
+ ContactGroupRssImpl theVolatileGroup = getNonPersistentGroup();
+
+
+ //if the parent volatile group is null then we create it
+ if (theVolatileGroup == null)
+ {
+ List emptyBuddies = new LinkedList();
+ theVolatileGroup = new ContactGroupRssImpl(
+ "NotInContactList"
+ , parentProvider);
+ theVolatileGroup.setResolved(false);
+ theVolatileGroup.setPersistent(false);
+ theVolatileGroup.addContact(newVolatileContact);
+
+ this.contactListRoot.addSubgroup(theVolatileGroup);
+
+ fireServerStoredGroupEvent(theVolatileGroup,
+ ServerStoredGroupEvent.GROUP_CREATED_EVENT);
+ }
+
+ //now add the volatile contact instide it
+ theVolatileGroup.addContact(newVolatileContact);
+ fireSubscriptionEvent(newVolatileContact,
+ theVolatileGroup,
+ SubscriptionEvent.SUBSCRIPTION_CREATED);
+
+ return newVolatileContact;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/OperationSetTypingNotificationsRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/OperationSetTypingNotificationsRssImpl.java
new file mode 100644
index 0000000..08f71ea
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/OperationSetTypingNotificationsRssImpl.java
@@ -0,0 +1,193 @@
+/*
+ * 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.rss;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements typing notifications for the Rss protocol. The operation
+ * set would simply mirror all outgoing typing notifications and make them
+ * appear as incoming events generated by the contact that we are currently
+ * writing a message to.
+ *
+ * @author Emil Ivov
+ */
+public class OperationSetTypingNotificationsRssImpl
+ implements OperationSetTypingNotifications
+{
+ private static final Logger logger =
+ Logger.getLogger(OperationSetTypingNotificationsRssImpl.class);
+
+ /**
+ * All currently registered TN listeners.
+ */
+ private List typingNotificationsListeners = new ArrayList();
+
+ /**
+ * The provider that created us.
+ */
+ private ProtocolProviderServiceRssImpl parentProvider = null;
+
+ /**
+ * The currently valid persistent presence operation set..
+ */
+ private OperationSetPersistentPresenceRssImpl opSetPersPresence = null;
+
+
+ /**
+ * Creates a new instance of this operation set and keeps the parent
+ * provider as a reference.
+ *
+ * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt>
+ * that created us and that we'll use for retrieving the underlying aim
+ * connection.
+ * @param opSetPersPresence the currently valid
+ * <tt>OperationSetPersistentPresenceRssImpl</tt> instance.
+ */
+ OperationSetTypingNotificationsRssImpl(
+ ProtocolProviderServiceRssImpl provider,
+ OperationSetPersistentPresenceRssImpl opSetPersPresence)
+ {
+ this.parentProvider = provider;
+ this.opSetPersPresence = opSetPersPresence;
+ }
+
+ /**
+ * Adds <tt>listener</tt> to the list of listeners registered for receiving
+ * <tt>TypingNotificationEvent</tt>s
+ *
+ * @param listener the <tt>TypingNotificationsListener</tt> listener that
+ * we'd like to add to the list of listeneres registered for receiving
+ * typing notificaions.
+ */
+ public void addTypingNotificationsListener(
+ TypingNotificationsListener listener)
+ {
+ synchronized(typingNotificationsListeners)
+ {
+ typingNotificationsListeners.add(listener);
+ }
+ }
+
+ /**
+ * Removes <tt>listener</tt> from the list of listeners registered for
+ * receiving <tt>TypingNotificationEvent</tt>s
+ *
+ * @param listener the <tt>TypingNotificationsListener</tt> listener that
+ * we'd like to remove
+ */
+ public void removeTypingNotificationsListener(
+ TypingNotificationsListener listener)
+ {
+ synchronized(typingNotificationsListeners)
+ {
+ typingNotificationsListeners.remove(listener);
+ }
+ }
+
+ /**
+ * 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
+ {
+ if( !(notifiedContact instanceof ContactRssImpl) )
+ throw new IllegalArgumentException(
+ "The specified contact is not a Rss contact."
+ + notifiedContact);
+
+
+ String userID = notifiedContact.getAddress();
+
+ //if the user id is owr own id, then this message is being routed to us
+ //from another instance of the rss provider.
+ if (userID.equals(this.parentProvider.getAccountID().getUserID()))
+ {
+ //check who is the provider sending the message
+ String sourceUserID = notifiedContact.getProtocolProvider()
+ .getAccountID().getUserID();
+
+ //check whether they are in our contact list
+ Contact from = opSetPersPresence.findContactByID(sourceUserID);
+
+ //and if not - add them there as volatile.
+ if (from == null)
+ {
+ from = opSetPersPresence.createVolatileContact(sourceUserID);
+ }
+
+ //and now fire the message received event.
+ fireTypingNotificationsEvent(from, typingState);
+ }
+ else
+ {
+ //if userID is not our own, try a check whether another provider
+ //has that id and if yes - deliver the message to them.
+ ProtocolProviderServiceRssImpl rssProvider
+ = this.opSetPersPresence.findProviderForRssUserID(userID);
+ if (rssProvider != null)
+ {
+ OperationSetTypingNotificationsRssImpl opSetTN
+ = (OperationSetTypingNotificationsRssImpl)
+ rssProvider.getOperationSet(
+ OperationSetTypingNotifications.class);
+ opSetTN.sendTypingNotification(notifiedContact, typingState);
+ }
+ else
+ {
+ //if we got here then "to" is simply someone in our contact
+ //list so let's just echo the message.
+ fireTypingNotificationsEvent(notifiedContact, typingState);
+ }
+ }
+ }
+
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/ProtocolIconRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/ProtocolIconRssImpl.java
new file mode 100644
index 0000000..b91b5df
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/ProtocolIconRssImpl.java
@@ -0,0 +1,102 @@
+/*
+ * 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.rss;
+
+import java.awt.image.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import javax.imageio.*;
+import javax.imageio.stream.*;
+
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.impl.gui.utils.ImageLoader.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Reperesents the Rss protocol icon. Implements the <tt>ProtocolIcon</tt>
+ * interface in order to provide a rss logo image in two different sizes.
+ *
+ * @author Yana Stamcheva
+ */
+public class ProtocolIconRssImpl
+ implements ProtocolIcon
+{
+ private static Logger logger
+ = Logger.getLogger(ProtocolIconRssImpl.class);
+
+ /**
+ * A hash table containing the protocol icon in different sizes.
+ */
+ private static Hashtable iconsTable = new Hashtable();
+ static
+ {
+ iconsTable.put(ProtocolIcon.ICON_SIZE_16x16,
+ loadIcon("resources/images/rss/rss-online.png"));
+
+ iconsTable.put(ProtocolIcon.ICON_SIZE_64x64,
+ loadIcon("resources/images/rss/rss64x64.png"));
+ }
+
+ /**
+ * Implements the <tt>ProtocolIcon.getSupportedSizes()</tt> method. Returns
+ * an iterator to a set containing the supported icon sizes.
+ * @return an iterator to a set containing the supported icon sizes
+ */
+ public Iterator getSupportedSizes()
+ {
+ return iconsTable.keySet().iterator();
+ }
+
+ /**
+ * Returne TRUE if a icon with the given size is supported, FALSE-otherwise.
+ */
+ public boolean isSizeSupported(String iconSize)
+ {
+ return iconsTable.containsKey(iconSize);
+ }
+
+ /**
+ * Returns the icon image in the given size.
+ * @param iconSize the icon size; one of ICON_SIZE_XXX constants
+ */
+ public byte[] getIcon(String iconSize)
+ {
+ return (byte[])iconsTable.get(iconSize);
+ }
+
+ /**
+ * Returns the icon image used to represent the protocol connecting state.
+ * @return the icon image used to represent the protocol connecting state
+ */
+ public byte[] getConnectingIcon()
+ {
+ return loadIcon("resources/images/rss/rss-online.png");
+ }
+
+ /**
+ * Loads an image from a given image path.
+ * @param imagePath The identifier of the image.
+ * @return The image for the given identifier.
+ */
+ public static byte[] loadIcon(String imagePath)
+ {
+ InputStream is = ProtocolIconRssImpl.class
+ .getClassLoader().getResourceAsStream(imagePath);
+
+ byte[] icon = null;
+ try {
+ icon = new byte[is.available()];
+ is.read(icon);
+ } catch (IOException e) {
+ logger.error("Failed to load icon: " + imagePath, e);
+ }
+ return icon;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/ProtocolProviderFactoryRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/ProtocolProviderFactoryRssImpl.java
new file mode 100644
index 0000000..de59fa7
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/ProtocolProviderFactoryRssImpl.java
@@ -0,0 +1,272 @@
+/*
+ * 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.rss;
+
+import java.util.*;
+
+import org.osgi.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * The Rss protocol provider factory creates instances of the Rss
+ * protocol provider service. One Service instance corresponds to one account.
+ *
+ * @author Emil Ivov
+ */
+public class ProtocolProviderFactoryRssImpl
+ extends ProtocolProviderFactory
+{
+ private static final Logger logger
+ = Logger.getLogger(ProtocolProviderFactoryRssImpl.class);
+
+ /**
+ * The table that we store our accounts in.
+ */
+ private Hashtable registeredAccounts = new Hashtable();
+
+
+ /**
+ * Creates an instance of the ProtocolProviderFactoryRssImpl.
+ */
+ public ProtocolProviderFactoryRssImpl()
+ {
+ super();
+ }
+
+ /**
+ * 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();
+ }
+
+ /**
+ * Returns a copy of the list containing the <tt>AccoudID</tt>s of all
+ * accounts currently registered in this protocol provider.
+ *
+ * @return a copy of the list containing the <tt>AccoudID</tt>s of all
+ * accounts currently registered in this protocol provider.
+ */
+ public ArrayList getRegisteredAccounts()
+ {
+ return new ArrayList(registeredAccounts.keySet());
+ }
+
+ /**
+ * Loads (and hence installs) all accounts previously stored in the
+ * configuration service.
+ */
+ public void loadStoredAccounts()
+ {
+ super.loadStoredAccounts( RssActivator.getBundleContext());
+ }
+
+
+ /**
+ * Initializaed and creates an account corresponding to the specified
+ * accountProperties and registers the resulting ProtocolProvider in the
+ * <tt>context</tt> BundleContext parameter.
+ *
+ * @param userIDStr tha/a user identifier uniquely representing the newly
+ * created account within the protocol namespace.
+ * @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
+ = RssActivator.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 RssAccountID(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 (through the UI) a call to the
+ //ProtocolProviderService.register() method and it needs to acces
+ //the configuration service and check for a stored password.
+ this.storeAccount(
+ RssActivator.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 loaded account
+ */
+ public AccountID loadAccount( Map accountProperties)
+ {
+ BundleContext context
+ = RssActivator.getBundleContext();
+ if(context == null)
+ throw new NullPointerException("The specified BundleContext was null");
+
+ String userIDStr = (String)accountProperties.get(USER_ID);
+
+ AccountID accountID = new RssAccountID(userIDStr, accountProperties);
+
+ //get a reference to the configuration service and register whatever
+ //properties we have in it.
+
+ Hashtable properties = new Hashtable();
+ properties.put(PROTOCOL, "Rss");
+ properties.put(USER_ID, userIDStr);
+
+ ProtocolProviderServiceRssImpl rssProtocolProvider
+ = new ProtocolProviderServiceRssImpl();
+
+ rssProtocolProvider.initialize(userIDStr, accountID);
+
+ ServiceRegistration registration
+ = context.registerService( ProtocolProviderService.class.getName(),
+ rssProtocolProvider,
+ properties);
+
+ registeredAccounts.put(accountID, registration);
+ return accountID;
+ }
+
+
+ /**
+ * Removes the specified account from the list of accounts that this
+ * provider factory is handling.
+ *
+ * @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)
+ {
+ //unregister the protocol provider
+ ServiceReference serRef = getProviderForAccount(accountID);
+
+ ProtocolProviderService protocolProvider
+ = (ProtocolProviderService) RssActivator.getBundleContext()
+ .getService(serRef);
+
+ try
+ {
+ protocolProvider.unregister();
+ }
+ catch (OperationFailedException exc)
+ {
+ logger.error("Failed to unregister protocol provider for account : "
+ + accountID + " caused by : " + exc);
+ }
+
+ ServiceRegistration registration
+ = (ServiceRegistration)registeredAccounts.remove(accountID);
+
+ if(registration == null)
+ return false;
+
+ //kill the service
+ registration.unregister();
+
+ registeredAccounts.remove(accountID);
+
+ return removeStoredAccount(RssActivator.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(RssActivator.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(RssActivator.getBundleContext(), accountID );
+ }
+
+ /**
+ * 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());
+ }
+ }
+
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/ProtocolProviderServiceRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/ProtocolProviderServiceRssImpl.java
new file mode 100644
index 0000000..669a956
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/ProtocolProviderServiceRssImpl.java
@@ -0,0 +1,447 @@
+/*
+ * 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.rss;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * A Rss implementation of the ProtocolProviderService.
+ *
+ * @author Emil Ivov/Jean-Albert Vescovo
+ */
+public class ProtocolProviderServiceRssImpl
+ implements ProtocolProviderService
+{
+ private static final Logger logger
+ = Logger.getLogger(ProtocolProviderServiceRssImpl.class);
+
+ /**
+ * The name of this protocol.
+ */
+ public static final String RSS_PROTOCOL_NAME = "Rss";
+
+ /**
+ * The id of the account that this protocol provider represents.
+ */
+ private AccountID accountID = null;
+
+ /**
+ * We use this to lock access to initialization.
+ */
+ private Object initializationLock = new Object();
+
+ /**
+ * The hashtable with the operation sets that we support locally.
+ */
+ private Hashtable supportedOperationSets = new Hashtable();
+
+ /**
+ * A list of listeners interested in changes in our registration state.
+ */
+ private Vector registrationStateListeners = new Vector();
+
+ /**
+ * Indicates whether or not the provider is initialized and ready for use.
+ */
+ private boolean isInitialized = false;
+
+ /**
+ * The logo corresponding to the rss protocol.
+ */
+ private ProtocolIconRssImpl rssIcon
+ = new ProtocolIconRssImpl();
+
+ /**
+ * A reference to the IM operation set
+ */
+ private OperationSetBasicInstantMessagingRssImpl basicInstantMessaging;
+
+ private boolean start = false;
+
+ /**
+ * The registration state that we are currently in. Note that in a real
+ * world protocol implementation this field won't exist and the registration
+ * state would be retrieved from the protocol stack.
+ */
+ private RegistrationState currentRegistrationState
+ = RegistrationState.UNREGISTERED;
+
+ /**
+ * The default constructor for the Rss protocol provider.
+ */
+ public ProtocolProviderServiceRssImpl()
+ {
+ logger.trace("Creating a rss provider.");
+ }
+
+ /**
+ * Initializes the service implementation, and puts it in a state 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 userID the user id of the rss account we're currently
+ * initializing
+ * @param accountID the identifier of the account that this protocol
+ * provider represents.
+ *
+ * @see net.java.sip.communicator.service.protocol.AccountID
+ */
+ protected void initialize(String userID,
+ AccountID accountID)
+ {
+ synchronized(initializationLock)
+ {
+ this.accountID = accountID;
+
+ //initialize the presence operationset
+ OperationSetPersistentPresenceRssImpl persistentPresence =
+ new OperationSetPersistentPresenceRssImpl(this);
+
+ supportedOperationSets.put(
+ OperationSetPersistentPresence.class.getName(),
+ persistentPresence);
+
+
+ //register it once again for those that simply need presence and
+ //won't be smart enough to check for a persistent presence
+ //alternative
+ supportedOperationSets.put( OperationSetPresence.class.getName(),
+ persistentPresence);
+
+ //initialize the IM operation set
+ //OperationSetBasicInstantMessagingRssImpl
+ basicInstantMessaging
+ = new OperationSetBasicInstantMessagingRssImpl(
+ this
+ , (OperationSetPersistentPresenceRssImpl)
+ persistentPresence);
+
+ supportedOperationSets.put(
+ OperationSetBasicInstantMessaging.class.getName(),
+ basicInstantMessaging);
+
+ //initialize the typing notifications operation set
+ OperationSetTypingNotifications typingNotifications =
+ new OperationSetTypingNotificationsRssImpl(
+ this, persistentPresence);
+
+ supportedOperationSets.put(
+ OperationSetTypingNotifications.class.getName(),
+ typingNotifications);
+
+ isInitialized = true;
+ }
+ }
+
+ /**
+ * 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(registrationStateListeners)
+ {
+ if (!registrationStateListeners.contains(listener))
+ registrationStateListeners.add(listener);
+ }
+
+ }
+
+ /**
+ * Removes the specified registration listener so that it won't receive
+ * further notifications when our registration state changes.
+ *
+ * @param listener the listener to remove.
+ */
+ public void removeRegistrationStateChangeListener(
+ RegistrationStateChangeListener listener)
+ {
+ synchronized(registrationStateListeners)
+ {
+ registrationStateListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Creates a <tt>RegistrationStateChangeEvent</tt> 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.
+ */
+ private void fireRegistrationStateChanged( RegistrationState oldState,
+ RegistrationState newState,
+ int reasonCode,
+ String reason)
+ {
+ RegistrationStateChangeEvent event =
+ new RegistrationStateChangeEvent(
+ this, oldState, newState, reasonCode, reason);
+
+ logger.debug("Dispatching " + event + " to "
+ + registrationStateListeners.size()+ " listeners.");
+
+ Iterator listeners = null;
+ synchronized (registrationStateListeners)
+ {
+ listeners = new ArrayList(registrationStateListeners).iterator();
+ }
+
+ while (listeners.hasNext())
+ {
+ RegistrationStateChangeListener listener
+ = (RegistrationStateChangeListener) listeners.next();
+
+ listener.registrationStateChanged(event);
+ }
+
+ /* If Timer isn't started, we launch a new timer for sending periodic
+ * rss feeds' refresh taks.
+ * If yes, we stop it.
+ */
+ if(!start){
+ this.basicInstantMessaging.createTimer();
+ start = true;
+ }
+ else{
+ this.basicInstantMessaging.stopTimer();
+ start = false;
+ }
+
+ logger.trace("Done.");
+ }
+
+
+ /**
+ * 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 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());
+ }
+
+ /**
+ * Returns the short name of the protocol that the implementation of this
+ * provider is based upon (like SIP, Jabber, ICQ/AIM, or others for
+ * example).
+ *
+ * @return a String containing the short name of the protocol this
+ * service is implementing (most often that would be a name in
+ * ProtocolNames).
+ */
+ public String getProtocolName()
+ {
+ return RSS_PROTOCOL_NAME;
+ }
+
+ /**
+ * Returns the state of the registration of this protocol provider with
+ * the corresponding registration service.
+ *
+ * @return ProviderRegistrationState
+ */
+ public RegistrationState getRegistrationState()
+ {
+ return currentRegistrationState;
+ }
+
+ /**
+ * Returns an array containing all operation sets supported by the
+ * current implementation.
+ *
+ * @return a java.util.Map containing instance of all supported
+ * operation sets mapped against their class names (e.g.
+ * OperationSetPresence.class.getName()) .
+ */
+ public Map getSupportedOperationSets()
+ {
+ //Copy the map so that the caller is not able to modify it.
+ return (Map)supportedOperationSets.clone();
+ }
+
+ /**
+ * Indicates whether or not this provider is registered
+ *
+ * @return true if the provider is currently registered and false
+ * otherwise.
+ */
+ public boolean isRegistered()
+ {
+ return currentRegistrationState.equals(RegistrationState.REGISTERED);
+ }
+
+ /**
+ * Starts the registration process.
+ *
+ * @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(SecurityAuthority authority)
+ throws OperationFailedException
+ {
+ //we don't really need a password here since there's no server in
+ //Rss but nevertheless we'll behave as if we did.
+
+ //verify whether a password has already been stored for this account
+ String password = RssActivator.
+ getProtocolProviderFactory().loadPassword(getAccountID());
+
+ //if we don't - retrieve it from the user through the security authority
+ 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("Rss"
+ , 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 the user indicated that the password should be saved, we'll ask
+ //the proto provider factory to store it for us.
+ if (credentials.isPasswordPersistent())
+ {
+ RssActivator.getProtocolProviderFactory()
+ .storePassword(getAccountID(), password);
+ }
+ }
+
+
+ RegistrationState oldState = currentRegistrationState;
+ currentRegistrationState = RegistrationState.REGISTERED;
+
+ fireRegistrationStateChanged(
+ oldState
+ , currentRegistrationState
+ , RegistrationStateChangeEvent.REASON_USER_REQUEST
+ , null);
+ }
+
+ /**
+ * 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()
+ {
+ if(!isInitialized)
+ {
+ return;
+ }
+ logger.trace("Killing the Rss Protocol Provider.");
+
+ if(isRegistered())
+ {
+ try
+ {
+ //do the unregistration
+ unregister();
+ }
+ catch (OperationFailedException ex)
+ {
+ //we're shutting down so we need to silence the exception here
+ logger.error(
+ "Failed to properly unregister before shutting down. "
+ + getAccountID()
+ , ex);
+ }
+ }
+
+ isInitialized = false;
+ }
+
+ /**
+ * Ends the registration of this protocol provider with the current
+ * registration service.
+ *
+ * @throws OperationFailedException with the corresponding code it the
+ * registration fails for some reason (e.g. a networking error or an
+ * implementation problem).
+ */
+ public void unregister()
+ throws OperationFailedException
+ {
+ RegistrationState oldState = currentRegistrationState;
+ currentRegistrationState = RegistrationState.UNREGISTERED;
+
+ fireRegistrationStateChanged(
+ oldState
+ , currentRegistrationState
+ , RegistrationStateChangeEvent.REASON_USER_REQUEST
+ , null);
+ }
+
+ /**
+ * Returns the rss protocol icon.
+ * @return the rss protocol icon
+ */
+ public ProtocolIcon getProtocolIcon()
+ {
+ return rssIcon;
+ }
+
+ /**
+ * Returns the IM set
+ * @return the IM set
+ */
+ public OperationSetBasicInstantMessagingRssImpl getBasicInstantMessaging()
+ {
+ return this.basicInstantMessaging;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/RssAccountID.java b/src/net/java/sip/communicator/impl/protocol/rss/RssAccountID.java
new file mode 100644
index 0000000..93765be
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/RssAccountID.java
@@ -0,0 +1,32 @@
+/*
+ * 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.rss;
+
+import net.java.sip.communicator.service.protocol.*;
+import java.util.Map;
+
+/**
+ * The Rss implementation of a sip-communicator account id.
+ * @author Emil Ivov
+ */
+public class RssAccountID
+ extends AccountID
+{
+ /**
+ * Creates an account id from the specified id and account properties.
+ *
+ * @param userID the user identifier correspnding to the account
+ * @param accountProperties any other properties necessary for the account.
+ */
+ RssAccountID(String userID, Map accountProperties)
+ {
+ super( userID,
+ accountProperties,
+ "Rss",
+ "rss.org");
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/RssActivator.java b/src/net/java/sip/communicator/impl/protocol/rss/RssActivator.java
new file mode 100644
index 0000000..973e28d
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/RssActivator.java
@@ -0,0 +1,116 @@
+/*
+ * 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.rss;
+
+import org.osgi.framework.*;
+import net.java.sip.communicator.util.*;
+import java.util.*;
+import net.java.sip.communicator.service.protocol.*;
+
+import net.java.sip.communicator.impl.gui.*;
+/**
+ * Loads the Rss provider factory and registers its services in the OSGI
+ * bundle context.
+ *
+ * @author Emil Ivov
+ */
+public class RssActivator
+ implements BundleActivator
+{
+ private static final Logger logger
+ = Logger.getLogger(RssActivator.class);
+
+ /**
+ * A reference to the registration of our Rss protocol provider
+ * factory.
+ */
+ private ServiceRegistration rssPpFactoryServReg = null;
+
+ /**
+ * A reference to the Rss protocol provider factory.
+ */
+ private static ProtocolProviderFactoryRssImpl
+ rssProviderFactory = null;
+
+ /**
+ * The currently valid bundle context.
+ */
+ private static BundleContext bundleContext = null;
+
+
+ /**
+ * Called when this bundle is started. In here we'll export the
+ * rss ProtocolProviderFactory implementation so that it could be
+ * possible to register accounts with it in SIP Communicator.
+ *
+ * @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, "Rss");
+
+ rssProviderFactory = new ProtocolProviderFactoryRssImpl();
+
+ //load all stored Rss accounts.
+ rssProviderFactory.loadStoredAccounts();
+
+ //reg the rss provider factory.
+ rssPpFactoryServReg = context.registerService(
+ ProtocolProviderFactory.class.getName(),
+ rssProviderFactory,
+ hashtable);
+
+ logger.info("Rss protocol implementation [STARTED].");
+ }
+
+ /**
+ * 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>ProtocolProviderFactoryJabberImpl</tt>
+ * instance that we have registered from this package.
+ */
+ public static ProtocolProviderFactoryRssImpl getProtocolProviderFactory()
+ {
+ return rssProviderFactory;
+ }
+
+ /**
+ * 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
+ {
+ this.rssProviderFactory.stop();
+ rssPpFactoryServReg.unregister();
+ logger.info("Rss protocol implementation [STOPPED].");
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/RssFeedReader.java b/src/net/java/sip/communicator/impl/protocol/rss/RssFeedReader.java
new file mode 100644
index 0000000..f955045
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/RssFeedReader.java
@@ -0,0 +1,256 @@
+/*
+ * 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.rss;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import com.sun.syndication.feed.synd.*;
+import com.sun.syndication.io.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+
+/**
+ * The class used for using the Informa Library into the RSS protocol
+ *
+ * @author Jean-Albert Vescovo
+ */
+public class RssFeedReader
+{
+
+ /**
+ * The id of the contact/feed, used to make a tcp query toward
+ * the .xml file containing the items of the feed.
+ */
+ private String address;
+
+ /**
+ * The title of the feed, which will be used as the displayname
+ * of the contact/feed.
+ */
+ private String title;
+
+ /**
+ * The object charged to retrieve the feed incoming from the relavant server.
+ */
+ private SyndFeed feed;
+
+ /**
+ * The last update date of this feed.
+ */
+ private Date ultimateItemDate = null;
+
+ /**
+ * An array of SyndEntry which will contain all the items retrieved from the feed.
+ */
+ private SyndEntry[] items;
+
+ /**
+ * Creates an instance of a rss feed with the specified string used
+ * as an url to contact the relevant server.
+ *
+ * @param address the url of this feed.
+ */
+ public RssFeedReader(String address)
+ {
+ this.address = address;
+ this.feed = null;
+ this.title = "No feed avalaible !";
+ }
+
+ /**
+ * To refresh this rss contact/feed registered as contact
+ * Moreover, we sort the items by reverse chronological order after
+ * insert them into an Array
+ */
+ public void recupFlux()
+ {
+ try
+ {
+ URL rssURL = new URL(this.address);
+
+ //the most important thing in this protocol: we parse the rss feed
+ //using the Rome library
+ SyndFeedInput input = new SyndFeedInput();
+ this.feed = input.build(new XmlReader(rssURL));
+ this.title = this.feed.getTitle();
+
+ //we retrieve the items and sort them by reverse chronological order
+ items = (SyndEntry[])(this.feed.getEntries().toArray(new SyndEntry[0]));
+ sortItems();
+
+ //we retrieve the date of the most recent item
+ this.ultimateItemDate = findUltimateItemDate();
+ }
+ catch(Exception ex)
+ {
+ ex.printStackTrace();
+ System.out.println("ERROR: "+ex.getMessage());
+ }
+ }
+
+ /**
+ * Returns a String containing the message to send to the user after
+ * a successful query on a rss server:
+ *
+ * - if we have no items, we return "No items found on this feed !"
+ * - if we can't read a date in these items, we return the last 10 items of the feed
+ * - if we can read a date, we just return the items which have a date earlier than
+ * the lastQueryDate, and "No new articles in your feed since last update." if it isn't
+ * new item since lastQueryDate.
+ *
+ * We signal to the user ("Send anything to refresh this feed...") that he can send anything
+ * to refresh the present contact/feed.
+ *
+ * @param lastQueryDate the date to compare with that of the items retrieved.
+ * @return String string
+ */
+ public String getPrintedFeed(Date lastQueryDate)
+ {
+ boolean more = true;
+ int i=0,nbNewItem = 0;
+ String printedFeed = new String();
+
+ if(items.length > 0)
+ {
+ while((i<items.length)&&more)
+ {
+ if((items[i].getPublishedDate() != null) && (lastQueryDate != null))
+ {
+ if(items[i].getPublishedDate().compareTo(lastQueryDate)>0)
+ {
+ printedFeed += "\nAt " + items[i].getPublishedDate()+" - " +
+ items[i].getTitle() +
+ "\nLink: " + items[i].getLink() + "\n\n";
+ nbNewItem++;
+ }
+ else{
+ more = false;
+ if(nbNewItem == 0) printedFeed +=
+ "\n\nNo new articles in your feed since last update.";
+ }
+ }
+ else{
+ if(items[i].getPublishedDate() != null)
+ printedFeed += "\nAt " + items[i].getPublishedDate();
+
+ printedFeed += "\n" + items[i].getTitle() +
+ "\nLink: "+items[i].getLink()+"\n\n";
+
+ if(i == 10) more = false;
+ }
+ i++;
+ }
+ printedFeed += ("\n\nSend anything to refresh this feed...");
+ }
+ else
+ {
+ printedFeed += "No items found on this feed !";
+ }
+ return printedFeed;
+ }
+
+ /**
+ * To sort the items retrieved from the rss contact/feed registered as contact
+ * We use for that a bubble sort algorithm
+ */
+ public void sortItems()
+ {
+ int i;
+ int size = items.length;
+ SyndEntry temp;
+ boolean inversion;
+ do
+ {
+ inversion=false;
+ for(i = 0; i < size - 1; i++)
+ {
+ if((items[i].getPublishedDate() != null) && (items[i+1].getPublishedDate()!=null))
+ if(items[i].getPublishedDate().compareTo(items[i+1].getPublishedDate())<0)
+ {
+ temp = items[i];
+ items[i] = items[i+1];
+ items[i+1] = temp;
+ inversion=true;
+ }
+ }
+ size--;
+ }while(inversion);
+ }
+
+ /**
+ * Returns a Date that can be used to know the most recent item in a retrieved feed.
+ *
+ * @return the feed's Date representing the nearest item's date never retrieved on this feed.
+ */
+ public Date getUltimateItemDate()
+ {
+ return this.ultimateItemDate;
+ }
+
+ /**
+ * Returns a Date that can be used to know the most recent item in a retrieved feed.
+ *
+ * This method just gives the date of the first element of the array of ItemIF previously
+ * sorted.
+ *
+ * @return a Date representing the nearest item's date.
+ */
+ private Date findUltimateItemDate()
+ {
+ if(items[0].getPublishedDate() != null)
+ this.ultimateItemDate = items[0].getPublishedDate();
+ return this.ultimateItemDate;
+ }
+
+ /**
+ * Returns a ChannelIF that can be used to know if a feed exists indeed.
+ *
+ * @return a ChannelIF containing the result of a query on a rss server.
+ */
+ public SyndFeed getFeed()
+ {
+ return this.feed;
+ }
+
+ /**
+ * Returns a Date giving the publication date of the feed on the relevant server.
+ *
+ * In most case, this date doesn't exist on the server. Not used at this time in this
+ * implementation.
+ *
+ * @return a Date representing the publication date of the feed.
+ */
+ public Date getPubDate()
+ {
+ return this.feed.getPublishedDate();
+ }
+
+ /**
+ * Returns a String used as a displayname.
+ *
+ * @return a String title representing the feed/contact.
+ */
+ public String getTitle()
+ {
+ return this.title;
+ }
+
+ /**
+ * Returns a String that can be used for identifying the contact.
+ *
+ * We'll prefer to use the title of the feed as displayname.
+ *
+ * @return a String id representing and uniquely identifying the contact.
+ */
+ public String getAddress()
+ {
+ return this.address;
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/RssStatusEnum.java b/src/net/java/sip/communicator/impl/protocol/rss/RssStatusEnum.java
new file mode 100644
index 0000000..fa76428
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/RssStatusEnum.java
@@ -0,0 +1,104 @@
+/*
+ * 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.rss;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+import java.io.*;
+
+/**
+ * An implementation of <tt>PresenceStatus</tt> that enumerates all states that
+ * a Rss contact can fall into.
+ *
+ * @author Jean-Albert Vescovo
+ */
+public class RssStatusEnum
+ extends PresenceStatus
+{
+ private static final Logger logger
+ = Logger.getLogger(RssStatusEnum.class);
+
+ /**
+ * Indicates an Offline status or status with 0 connectivity.
+ */
+ public static final RssStatusEnum OFFLINE
+ = new RssStatusEnum(
+ 0
+ , "Offline"
+ , loadIcon("resources/images/rss/rss-offline.png"));
+
+ /**
+ * The Online status. Indicate that the user is able and willing to
+ * communicate.
+ */
+ public static final RssStatusEnum ONLINE
+ = new RssStatusEnum(
+ 65
+ , "Online"
+ , loadIcon("resources/images/rss/rss-online.png"));
+
+ /**
+ * Initialize the list of supported status states.
+ */
+ private static List supportedStatusSet = new LinkedList();
+ static
+ {
+ supportedStatusSet.add(OFFLINE);
+ supportedStatusSet.add(ONLINE);
+ }
+
+ /**
+ * Creates an instance of <tt>RssPresneceStatus</tt> with the
+ * specified parameters.
+ * @param status the connectivity level of the new presence status instance
+ * @param statusName the name of the presence status.
+ * @param statusIcon the icon associated with this status
+ */
+ private RssStatusEnum(int status,
+ String statusName,
+ byte[] statusIcon)
+ {
+ super(status, statusName, statusIcon);
+ }
+
+ /**
+ * Returns an iterator over all status instances supproted by the rss
+ * provider.
+ * @return an <tt>Iterator</tt> over all status instances supported by the
+ * rss provider.
+ */
+ static Iterator supportedStatusSet()
+ {
+ return supportedStatusSet.iterator();
+ }
+
+ /**
+ * Loads an image from a given image path.
+ * @param imagePath The path to the image resource.
+ * @return The image extracted from the resource at the specified path.
+ */
+ public static byte[] loadIcon(String imagePath)
+ {
+ InputStream is = RssStatusEnum.class.getClassLoader()
+ .getResourceAsStream(imagePath);
+
+ byte[] icon = null;
+ try
+ {
+ icon = new byte[is.available()];
+ is.read(icon);
+ }
+ catch (IOException exc)
+ {
+ logger.error("Failed to load icon: " + imagePath, exc);
+ }
+ return icon;
+ }
+
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/RssThread.java b/src/net/java/sip/communicator/impl/protocol/rss/RssThread.java
new file mode 100644
index 0000000..7994764
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/RssThread.java
@@ -0,0 +1,73 @@
+/*
+ * 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.rss;
+
+import java.util.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+
+/**
+ * Instant messaging functionalites for the Rss protocol.
+ *
+ * @author Jean-Albert Vescovo
+ */
+public class RssThread
+ extends Thread
+{
+ private OperationSetBasicInstantMessagingRssImpl opSet;
+ private ContactRssImpl rssFeed = null;
+ private boolean newContact = false;
+ private boolean aloneUpdate = false;
+
+ /** Creates a new instance of RssThread
+ * @param opSet the OperationSetBasicInstantMessagingRssImpl instance that
+ * is managing the rss protocol.
+ */
+ public RssThread(OperationSetBasicInstantMessagingRssImpl opSet)
+ {
+ this.opSet = opSet;
+ this.start();
+ }
+
+ /** Creates a new instance of RssThread
+ * @param opSet the OperationSetBasicInstantMessagingRssImpl instance that
+ * is managing the rss protocol.
+ * @param rssFeed the contact that the thread is going to do a query
+ * @param newContact newContact
+ * @param aloneUpdate aloneUpdate
+ */
+ public RssThread(OperationSetBasicInstantMessagingRssImpl opSet,
+ ContactRssImpl rssFeed,
+ boolean newContact,
+ boolean aloneUpdate)
+ {
+ this.opSet = opSet;
+ this.rssFeed = rssFeed;
+ this.newContact = newContact;
+ this.aloneUpdate = aloneUpdate;
+ this.start();
+ }
+
+ /**
+ * The task executed by the thread
+ * If no rss contact given as parameter, the query is launched for all contacts
+ */
+ public void run()
+ {
+ try
+ {
+ if(this.rssFeed == null)
+ this.opSet.refreshRssFeed();
+ else
+ this.opSet.refreshRssFeed(this.rssFeed,this.newContact,this.aloneUpdate);
+ }
+ catch(Exception exc)
+ {
+ exc.printStackTrace();
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/RssTimerRefreshFeed.java b/src/net/java/sip/communicator/impl/protocol/rss/RssTimerRefreshFeed.java
new file mode 100644
index 0000000..264a893
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/RssTimerRefreshFeed.java
@@ -0,0 +1,41 @@
+/*
+ * 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.rss;
+
+import java.util.TimerTask;
+
+/**
+ * Instant messaging functionalites for the Rss protocol.
+ *
+ * @author Jean-Albert Vescovo
+ */
+
+public class RssTimerRefreshFeed
+ extends TimerTask
+{
+ private OperationSetBasicInstantMessagingRssImpl opSet;
+
+ /**
+ * Creates an instance of timer used to seeking periodically the rss feeds registered
+ * as contacts.
+ * @param opSet the OperationSetBasicInstantMessagingRssImpl instance that
+ * is managing the rss protocol.
+ */
+ public RssTimerRefreshFeed(OperationSetBasicInstantMessagingRssImpl opSet)
+ {
+ this.opSet = opSet;
+ }
+
+ /**
+ * What the timer is supposed to do each time the PERIOD_REFRESH_RSS expire.
+ * In facts, it launch a new thread responsible for starting one or more rss queries
+ */
+ public void run()
+ {
+ RssThread rssThr = new RssThread(this.opSet);
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/protocol/rss/rss.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/rss/rss.provider.manifest.mf
new file mode 100644
index 0000000..f54a798
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/rss/rss.provider.manifest.mf
@@ -0,0 +1,15 @@
+Bundle-Activator: net.java.sip.communicator.impl.protocol.rss.RssActivator
+Bundle-Name: Rss Protocol Provider
+Bundle-Description: A bundle providing support for the Rss protocol.
+Bundle-Vendor: sip-communicator.org
+Bundle-Version: 0.0.1
+Import-Package: org.osgi.framework,
+ org.xml.sax,
+ org.xml.sax.helpers,
+ org.jdom,
+ org.jdom.input,
+ net.java.sip.communicator.service.configuration,
+ net.java.sip.communicator.service.configuration.event,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.service.protocol,
+ net.java.sip.communicator.service.protocol.event
diff --git a/src/net/java/sip/communicator/plugin/rssaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/rssaccregwizz/FirstWizardPage.java
new file mode 100644
index 0000000..49c7c44
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/rssaccregwizz/FirstWizardPage.java
@@ -0,0 +1,265 @@
+/*
+ * 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.rssaccregwizz;
+
+import java.util.*;
+
+import java.awt.*;
+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 user ID
+ * and the password of the account.
+ *
+ * @author Emil Ivov/Jean-Albert Vescovo
+ */
+public class FirstWizardPage
+ extends JPanel implements WizardPage, DocumentListener
+{
+
+ public static final String FIRST_PAGE_IDENTIFIER = "FirstPageIdentifier";
+
+ private JPanel userPassPanel = new JPanel(new BorderLayout(10, 10));
+
+ private JPanel labelsPanel = new JPanel();
+
+ private JLabel existingAccountLabel =
+ new JLabel("RSS account already exists !");
+
+ private JLabel creatingAccountLabel =
+ new JLabel("Press next to creat your RSS account...");
+
+ private JTextField userIDField = new JTextField();
+
+ private JPanel mainPanel = new JPanel();
+
+ private Object nextPageIdentifier = WizardPage.SUMMARY_PAGE_IDENTIFIER;
+
+ private RssAccountRegistration registration = null;
+
+ private WizardContainer wizardContainer;
+
+ /**
+ * Creates an instance of <tt>FirstWizardPage</tt>.
+ * @param registration the <tt>RssAccountRegistration</tt>, where
+ * all data through the wizard are stored
+ * @param wizardContainer the wizardContainer, where this page will
+ * be added
+ */
+ public FirstWizardPage(RssAccountRegistration 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));
+ }
+
+ /**
+ * Initializes all panels, buttons, etc.
+ */
+ private void init()
+ {
+ this.userIDField.getDocument().addDocumentListener(this);
+
+ this.existingAccountLabel.setForeground(Color.RED);
+
+ this.creatingAccountLabel.setForeground(Color.BLUE);
+
+ labelsPanel.add(creatingAccountLabel);
+
+ if(!isExistingAccount("rss")){
+ labelsPanel.remove(existingAccountLabel);
+ labelsPanel.add(creatingAccountLabel);
+ setNextButtonAccordingToUserID(true);
+ }
+ else{
+ labelsPanel.remove(creatingAccountLabel);
+ labelsPanel.add(existingAccountLabel);
+ setNextButtonAccordingToUserID(false);
+ }
+
+ userPassPanel.add(labelsPanel, BorderLayout.CENTER);
+
+ userPassPanel.setBorder(BorderFactory
+ .createTitledBorder("RSS account creation..."));
+
+ this.add(userPassPanel, BorderLayout.CENTER);
+ }
+
+ /**
+ * Implements the <code>WizardPage.getIdentifier</code> to return
+ * this page identifier.
+ *
+ * @return the Identifier of the first page in this wizard.
+ */
+ public Object getIdentifier()
+ {
+ return FIRST_PAGE_IDENTIFIER;
+ }
+
+ /**
+ * Implements the <code>WizardPage.getNextPageIdentifier</code> to return
+ * the next page identifier - the summary page.
+ *
+ * @return the identifier of the page following this one.
+ */
+ public Object getNextPageIdentifier()
+ {
+ return nextPageIdentifier;
+ }
+
+ /**
+ * Implements the <code>WizardPage.getBackPageIdentifier</code> to return
+ * the next back identifier - the default page.
+ *
+ * @return the identifier of the default wizard page.
+ */
+ public Object getBackPageIdentifier()
+ {
+ return WizardPage.DEFAULT_PAGE_IDENTIFIER;
+ }
+
+ /**
+ * Implements the <code>WizardPage.getWizardForm</code> to return
+ * this panel.
+ *
+ * @return the component to be displayed in this wizard page.
+ */
+ public Object getWizardForm()
+ {
+ return this;
+ }
+
+ /**
+ * Before this page is displayed enables or disables the "Next" wizard
+ * button according to whether the UserID field is empty.
+ */
+ public void pageShowing()
+ {
+ if(isExistingAccount("Rss")) setNextButtonAccordingToUserID(false);
+ else setNextButtonAccordingToUserID(true);
+ }
+
+ /**
+ * Saves the user input when the "Next" wizard buttons is clicked.
+ */
+ public void pageNext()
+ {
+ nextPageIdentifier = SUMMARY_PAGE_IDENTIFIER;
+ userPassPanel.remove(existingAccountLabel);
+ registration.setUserID("Rss");
+ registration.setPassword("rss");
+ }
+
+ /**
+ * Enables or disables the "Next" wizard button according to whether the
+ * User ID field is empty.
+ */
+ private void setNextButtonAccordingToUserID(boolean newOne)
+ {
+ if(!newOne)
+ {
+ wizardContainer.setNextFinishButtonEnabled(false);
+ }
+ else
+ {
+ wizardContainer.setNextFinishButtonEnabled(true);
+ }
+ }
+
+ /**
+ * Handles the <tt>DocumentEvent</tt> triggered when user types in the
+ * User ID field. Enables or disables the "Next" wizard button according to
+ * whether the User ID field is empty.
+ *
+ * @param event the event containing the update.
+ */
+ public void insertUpdate(DocumentEvent event)
+ {
+ }
+
+ /**
+ * Handles the <tt>DocumentEvent</tt> triggered when user deletes letters
+ * from the UserID field. Enables or disables the "Next" wizard button
+ * according to whether the UserID field is empty.
+ *
+ * @param event the event containing the update.
+ */
+ public void removeUpdate(DocumentEvent event)
+ {
+ }
+
+ public void changedUpdate(DocumentEvent event)
+ {
+ }
+
+ public void pageHiding()
+ {
+ }
+
+ public void pageShown()
+ {
+ }
+
+ public void pageBack()
+ {
+ }
+
+ /**
+ * Fills the UserID 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)
+ {
+ }
+
+ /**
+ * Verifies whether there is already an account installed with the same
+ * details as the one that the user has just entered.
+ *
+ * @param userID the name of the user that the account is registered for
+ * @return true if there is already an account for this userID and false
+ * otherwise.
+ */
+ private boolean isExistingAccount(String userID)
+ {
+ ProtocolProviderFactory factory
+ = RssAccRegWizzActivator.getRssProtocolProviderFactory();
+
+ ArrayList registeredAccounts = factory.getRegisteredAccounts();
+
+ for (int i = 0; i < registeredAccounts.size(); i++)
+ {
+ AccountID accountID = (AccountID) registeredAccounts.get(i);
+
+ if (userID.equalsIgnoreCase(accountID.getUserID()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/rssaccregwizz/Resources.java b/src/net/java/sip/communicator/plugin/rssaccregwizz/Resources.java
new file mode 100644
index 0000000..cde8560
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/rssaccregwizz/Resources.java
@@ -0,0 +1,95 @@
+/*
+ * 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.rssaccregwizz;
+
+import java.io.*;
+import java.util.*;
+
+import net.java.sip.communicator.util.*;
+
+/**
+ * The Messages class manages the access to the internationalization
+ * properties files.
+ *
+ * @author Emil Ivov
+ */
+public class Resources
+{
+
+ private static Logger log = Logger.getLogger(Resources.class);
+
+ private static final String BUNDLE_NAME
+ = "net.java.sip.communicator.plugin.rssaccregwizz.resources";
+
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
+ .getBundle(BUNDLE_NAME);
+
+ public static ImageID GIBBERISH_LOGO = new ImageID("protocolIcon");
+
+ public static ImageID PAGE_IMAGE = new ImageID("pageImage");
+
+ /**
+ * 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 exc)
+ {
+ 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 exc)
+ {
+ log.error("Failed to load image:" + path, exc);
+ }
+
+ 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/rssaccregwizz/RssAccRegWizzActivator.java b/src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccRegWizzActivator.java
new file mode 100644
index 0000000..b68dd37
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccRegWizzActivator.java
@@ -0,0 +1,109 @@
+/*
+ * 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.rssaccregwizz;
+
+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>RssAccountRegistrationWizard</tt> in the UI Service.
+ *
+ * @author Emil Ivov
+ */
+public class RssAccRegWizzActivator
+ implements BundleActivator
+{
+ private static Logger logger = Logger.getLogger(
+ RssAccRegWizzActivator.class.getName());
+
+ /**
+ * A currently valid bundle context.
+ */
+ public static BundleContext bundleContext;
+
+ /**
+ * A currently valid reference to the configuration service.
+ */
+ private static ConfigurationService configService;
+
+ /**
+ * Starts this bundle.
+ * @param bc the currently valid <tt>BundleContext</tt>.
+ */
+ public void start(BundleContext bc)
+ {
+ logger.info("Loading rss account wizard.");
+
+ bundleContext = bc;
+
+ ServiceReference uiServiceRef = bundleContext
+ .getServiceReference(UIService.class.getName());
+
+ UIService uiService
+ = (UIService) bundleContext.getService(uiServiceRef);
+
+ AccountRegistrationWizardContainer wizardContainer
+ = uiService.getAccountRegWizardContainer();
+
+ RssAccountRegistrationWizard rssWizard
+ = new RssAccountRegistrationWizard(wizardContainer);
+
+ wizardContainer.addAccountRegistrationWizard(rssWizard);
+
+ logger.info("Rss account registration wizard [STARTED].");
+ }
+
+ /**
+ * 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.
+ */
+ public void stop(BundleContext context)
+ {
+
+ }
+
+ /**
+ * Returns the <tt>ProtocolProviderFactory</tt> for the Rss protocol.
+ * @return the <tt>ProtocolProviderFactory</tt> for the Rss protocol
+ */
+ public static ProtocolProviderFactory getRssProtocolProviderFactory()
+ {
+
+ ServiceReference[] serRefs = null;
+
+ String osgiFilter = "("
+ + ProtocolProviderFactory.PROTOCOL
+ + "=" + "Rss" + ")";
+
+ try
+ {
+ serRefs = bundleContext.getServiceReferences(
+ ProtocolProviderFactory.class.getName(), osgiFilter);
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ logger.error(ex);
+ }
+
+ return (ProtocolProviderFactory) bundleContext.getService(serRefs[0]);
+ }
+
+ /**
+ * Returns the bundleContext that we received when we were started.
+ *
+ * @return a currently valid instance of a bundleContext.
+ */
+ public BundleContext getBundleContext()
+ {
+ return bundleContext;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccountRegistration.java b/src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccountRegistration.java
new file mode 100644
index 0000000..b815f1b
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccountRegistration.java
@@ -0,0 +1,82 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+/**
+ * The <tt>RssAccountRegistration</tt> is used to store all user input data
+ * through the <tt>RssAccountRegistrationWizard</tt>.
+ *
+ * @author Emil Ivov/Jean-Albert Vescovo
+ */
+package net.java.sip.communicator.plugin.rssaccregwizz;
+
+public class RssAccountRegistration
+{
+ private String userID;
+ private String password;
+ private boolean rememberPassword;
+
+ /**
+ * Returns the User ID of the rss registration account.
+ * @return the User ID of the rss registration account.
+ */
+ public String getUserID()
+ {
+ return userID;
+ }
+
+ /**
+ * Sets the user ID of the rss registration account.
+ * @param userID the userID of the rss registration account.
+ */
+ public void setUserID(String userID)
+ {
+ this.userID = userID;
+ }
+
+ /**
+ * Returns the password of the Rss registration account.
+ *
+ * @return the password of the Rss registration account.
+ */
+ public String getPassword()
+ {
+ return password;
+ }
+
+ /**
+ * Sets the password of the Rss registration account.
+ *
+ * @param password the password of the Rss registration account.
+ */
+ public void setPassword(String password)
+ {
+ this.password = password;
+ }
+
+ /**
+ * Returns <tt>true</tt> if password has to remembered, <tt>false</tt>
+ * otherwise.
+ *
+ * @return <tt>true</tt> if password has to remembered, <tt>false</tt>
+ * otherwise.
+ */
+ public boolean isRememberPassword()
+ {
+ return true;
+ }
+
+ /**
+ * Sets the rememberPassword value of this Rss account registration.
+ *
+ * @param rememberPassword <tt>true</tt> if password has to remembered,
+ * <tt>false</tt> otherwise.
+ */
+ public void setRememberPassword(boolean rememberPassword)
+ {
+ this.rememberPassword = true;
+ }
+
+}
diff --git a/src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccountRegistrationWizard.java b/src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccountRegistrationWizard.java
new file mode 100644
index 0000000..5c0dd77
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/rssaccregwizz/RssAccountRegistrationWizard.java
@@ -0,0 +1,198 @@
+/*
+ * 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.rssaccregwizz;
+
+import java.util.*;
+
+import org.osgi.framework.*;
+import net.java.sip.communicator.impl.gui.customcontrols.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * The <tt>RssAccountRegistrationWizard</tt> is an implementation of the
+ * <tt>AccountRegistrationWizard</tt> for the Rss protocol. It allows
+ * the user to create and configure a new Rss account.
+ *
+ * @author Emil Ivov
+ */
+public class RssAccountRegistrationWizard
+ implements AccountRegistrationWizard
+{
+
+ /**
+ * The first page of the rss account registration wizard.
+ */
+ private FirstWizardPage firstWizardPage;
+
+ /**
+ * The object that we use to store details on an account that we will be
+ * creating.
+ */
+ private RssAccountRegistration registration
+ = new RssAccountRegistration();
+
+ private WizardContainer wizardContainer;
+
+ private ProtocolProviderService protocolProvider;
+
+ private String propertiesPackage
+ = "net.java.sip.communicator.plugin.rssaccregwizz";
+
+ private boolean isModification;
+
+ /**
+ * Creates an instance of <tt>RssAccountRegistrationWizard</tt>.
+ * @param wizardContainer the wizard container, where this wizard
+ * is added
+ */
+ public RssAccountRegistrationWizard(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.GIBBERISH_LOGO);
+ }
+
+ /**
+ * Implements the <code>AccountRegistrationWizard.getPageImage</code> method.
+ * Returns the image used to decorate the wizard page
+ *
+ * @return byte[] the image used to decorate the wizard page
+ */
+ public byte[] getPageImage()
+ {
+ return Resources.getImage(Resources.PAGE_IMAGE);
+ }
+
+ /**
+ * 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("User ID", registration.getUserID());
+
+ return summaryTable.entrySet().iterator();
+ }
+
+ /**
+ * Installs the account created through this wizard.
+ * @return ProtocolProviderService
+ */
+ public ProtocolProviderService finish()
+ {
+ firstWizardPage = null;
+ ProtocolProviderFactory factory
+ = RssAccRegWizzActivator.getRssProtocolProviderFactory();
+
+ return this.installAccount(factory,
+ registration.getUserID());
+ }
+
+ /**
+ * Creates an account for the given user and password.
+ * @param providerFactory the ProtocolProviderFactory which will create
+ * the account
+ * @param user the user identifier
+ * @return the <tt>ProtocolProviderService</tt> for the new account.
+ */
+ public ProtocolProviderService installAccount(
+ ProtocolProviderFactory providerFactory,
+ String user)
+ {
+
+ Hashtable accountProperties = new Hashtable();
+
+ if (registration.isRememberPassword())
+ {
+ accountProperties.put(ProtocolProviderFactory.PASSWORD
+ , registration.getPassword());
+ }
+
+ try
+ {
+ AccountID accountID = providerFactory.installAccount(
+ user, accountProperties);
+
+ ServiceReference serRef = providerFactory
+ .getProviderForAccount(accountID);
+
+ protocolProvider = (ProtocolProviderService)
+ RssAccRegWizzActivator.bundleContext
+ .getService(serRef);
+ }
+ catch (IllegalArgumentException exc)
+ {
+ new ErrorDialog(null, exc.getMessage(), exc).showDialog();
+ }
+ catch (IllegalStateException exc)
+ {
+ new ErrorDialog(null, exc.getMessage(), exc).showDialog();
+ }
+
+ return protocolProvider;
+ }
+
+ /**
+ * Fills the UserID 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;
+
+ isModification = true;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/rssaccregwizz/resources.properties b/src/net/java/sip/communicator/plugin/rssaccregwizz/resources.properties
new file mode 100644
index 0000000..5cf3b0b
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/rssaccregwizz/resources.properties
@@ -0,0 +1,10 @@
+protocolName=Rss
+protocolDescription=Add your preferred RSS feeds into SIP Communicator !
+userID=User ID:
+password=Password:
+rememberPassword=Remember password
+userAndPassword=Identification
+existingAccount=* The account you entered is already installed.
+
+protocolIcon=resources/images/rss/rss-online.png
+pageImage=resources/images/rss/rss64x64.png
diff --git a/src/net/java/sip/communicator/plugin/rssaccregwizz/rssaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/rssaccregwizz/rssaccregwizz.manifest.mf
new file mode 100644
index 0000000..71bebc6
--- /dev/null
+++ b/src/net/java/sip/communicator/plugin/rssaccregwizz/rssaccregwizz.manifest.mf
@@ -0,0 +1,30 @@
+Bundle-Activator: net.java.sip.communicator.plugin.rssaccregwizz.RssAccRegWizzActivator
+Bundle-Name: Rss account registration wizard
+Bundle-Description: Rss 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.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,
+ net.java.sip.communicator.service.browserlauncher,
+ 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