diff options
author | Vincent Lucas <chenzo@jitsi.org> | 2008-05-26 14:11:33 +0000 |
---|---|---|
committer | Vincent Lucas <chenzo@jitsi.org> | 2008-05-26 14:11:33 +0000 |
commit | 137cf4192707221c9a39b839c2ea26bc48a4856e (patch) | |
tree | b4e28903f9c6ec31bf9f9acf886328bb9d6d86be /src/net/java/sip | |
parent | f126eda471f403003cd672b8625556bef9ef8228 (diff) | |
download | jitsi-137cf4192707221c9a39b839c2ea26bc48a4856e.zip jitsi-137cf4192707221c9a39b839c2ea26bc48a4856e.tar.gz jitsi-137cf4192707221c9a39b839c2ea26bc48a4856e.tar.bz2 |
Adding DICT protocol and wizard.
Diffstat (limited to 'src/net/java/sip')
28 files changed, 6990 insertions, 1 deletions
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/ContactDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ContactDictImpl.java new file mode 100644 index 0000000..0d9cc2b --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/ContactDictImpl.java @@ -0,0 +1,322 @@ +/* + * 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.dict; + +import net.java.sip.communicator.service.protocol.*; + +/** + * An implementation of a Dict contact + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class ContactDictImpl + implements Contact +{ + /** + * The id of the contact. + */ + private String contactID = null; + + /** + * The provider that created us. + */ + private ProtocolProviderServiceDictImpl parentProvider = null; + + /** + * The group that belong to. + */ + private ContactGroupDictImpl parentGroup = null; + + /** + * The presence status of the contact. + */ + private PresenceStatus presenceStatus = DictStatusEnum.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 = true; + + /** + * Socket connection associated with the contact + */ + private DictAdapter dictAdapter = null; + + /** + * 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 ContactDictImpl( + String databaseCode, + ProtocolProviderServiceDictImpl parentProvider) + { + this.contactID = databaseCode; + this.parentProvider = parentProvider; + } + + /** + * This method is only called when the contact is added to a new + * <tt>ContactGroupDictImpl</tt> by the + * <tt>ContactGroupDictImpl</tt> itself. + * + * @param newParentGroup the <tt>ContactGroupDictImpl</tt> that is now + * parent of this <tt>ContactDictImpl</tt> + */ + void setParentGroup(ContactGroupDictImpl 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 getContactID() + { + return contactID; + } + + /** + * 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() + { + return contactID; + } + + /** + * 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 always DictStatusEnum.ONLINE. + */ + public PresenceStatus getPresenceStatus() + { + return this.presenceStatus; + } + + /** + * Sets <tt>dictPresenceStatus</tt> as the PresenceStatus that this + * contact is currently in. + * @param dictPresenceStatus the <tt>DictPresenceStatus</tt> + * currently valid for this contact. + */ + public void setPresenceStatus(PresenceStatus dictPresenceStatus) + { + this.presenceStatus = dictPresenceStatus; + } + + /** + * 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; + } + + /** + * Return a reference to the socket connexion linked with the Account + * @return a reference to the socket connexion (DictAdapter) + */ + public DictAdapter getDictAdapter() + { + return parentProvider.getDictAdapter(); + } + + /** + * 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>ContactGroupDictImpl</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("ContactDictImpl[ 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() + { + 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; + } + + /** + * Return the current status message of this contact. + * + * @return null as the protocol has currently no support of status messages + */ + public String getStatusMessage() + { + return null; + } + + /** + * 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 ContactDictImpl)) { + return false; + } + + ContactDictImpl dictContact = (ContactDictImpl) obj; + + return this.getAddress().equals(dictContact.getAddress()); + } + + + /** + * Returns the persistent presence operation set that this contact belongs + * to. + * + * @return the <tt>OperationSetPersistentPresenceGibberishImpl</tt> that + * this contact belongs to. + */ + public OperationSetPersistentPresenceDictImpl + getParentPresenceOperationSet() + { + return (OperationSetPersistentPresenceDictImpl) parentProvider + .getOperationSet(OperationSetPersistentPresence.class); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/ContactGroupDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ContactGroupDictImpl.java new file mode 100644 index 0000000..2c706f8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/ContactGroupDictImpl.java @@ -0,0 +1,588 @@ +/* + * 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.dict; + +import java.util.*; + +import net.java.sip.communicator.service.protocol.*; + +/** + * A simple, straightforward implementation of a dict ContactGroup. Since + * the Dict 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 ROTH Damien + * @author LITZELMANN Cédric + */ +public class ContactGroupDictImpl + implements ContactGroup +{ + + /** + * The name of this Dict 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 ContactGroupDictImpl 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 ProtocolProviderServiceDictImpl 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 ContactGroupDictImpl with the specified name. + * + * @param groupName the name of the group. + * @param parentProvider the protocol provider that created this group. + */ + public ContactGroupDictImpl( + String groupName, + ProtocolProviderServiceDictImpl 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 ContactDictImpl to add to this group. + */ + public void addContact(ContactDictImpl 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 ContactGroupDictImpl to add as a subgroup to this group. + */ + public void addSubgroup(ContactGroupDictImpl subgroup) + { + this.subGroups.add(subgroup); + subgroup.setParentGroup(this); + } + + /** + * Sets the group that is the new parent of this group + * @param parent ContactGroupDictImpl + */ + void setParentGroup(ContactGroupDictImpl 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 ContactGroupDictImpl subgroup to remove. + */ + public void removeSubGroup(ContactGroupDictImpl 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 (ContactDictImpl)contacts.get(index); + } + + /** + * Returns the group that is parent of the specified dictGroup or null + * if no parent was found. + * @param dictGroup the group whose parent we're looking for. + * @return the ContactGroupDictImpl instance that dictGroup + * belongs to or null if no parent was found. + */ + public ContactGroupDictImpl findGroupParent( + ContactGroupDictImpl dictGroup) + { + if ( subGroups.contains(dictGroup) ) + return this; + + Iterator subGroupsIter = subgroups(); + while (subGroupsIter.hasNext()) + { + ContactGroupDictImpl subgroup + = (ContactGroupDictImpl) subGroupsIter.next(); + + ContactGroupDictImpl parent + = subgroup.findGroupParent(dictGroup); + + if(parent != null) + return parent; + } + return null; + } + + /** + * Returns the group that is parent of the specified dictContact or + * null if no parent was found. + * + * @param dictContact the contact whose parent we're looking for. + * @return the ContactGroupDictImpl instance that dictContact + * belongs to or <tt>null</tt> if no parent was found. + */ + public ContactGroupDictImpl findContactParent( + ContactDictImpl dictContact) + { + if ( contacts.contains(dictContact) ) + return this; + + Iterator subGroupsIter = subgroups(); + while (subGroupsIter.hasNext()) + { + ContactGroupDictImpl subgroup + = (ContactGroupDictImpl) subGroupsIter.next(); + + ContactGroupDictImpl parent + = subgroup.findContactParent(dictContact); + + 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()) + { + ContactDictImpl contact = (ContactDictImpl) 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()) + { + ContactGroupDictImpl contactGroup + = (ContactGroupDictImpl) 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 ContactDictImpl to remove from this group + */ + public void removeContact(ContactDictImpl 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 ContactDictImpl + */ + public ContactDictImpl findContactByID(String id) + { + //first go through the contacts that are direct children. + Iterator contactsIter = contacts(); + + while(contactsIter.hasNext()) + { + ContactDictImpl mContact = (ContactDictImpl)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() ) + { + ContactGroupDictImpl mGroup = (ContactGroupDictImpl)groupsIter.next(); + + ContactDictImpl 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()) + { + ContactGroupDictImpl group = (ContactGroupDictImpl)subGroups.next(); + buff.append(group.toString()); + if (subGroups.hasNext()) + buff.append("\n"); + } + + buff.append("\nChildContacts="+countContacts()+":["); + + Iterator contacts = contacts(); + while (contacts.hasNext()) + { + ContactDictImpl contact = (ContactDictImpl) contacts.next(); + buff.append(contact.toString()); + if(contacts.hasNext()) + buff.append(", "); + } + return buff.append("]").toString(); + } + + /** + * 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 ContactGroupDictImpl)) + return false; + + ContactGroupDictImpl dictGroup + = (ContactGroupDictImpl)obj; + + if( ! dictGroup.getGroupName().equals(getGroupName()) + || ! dictGroup.getUID().equals(getUID()) + || dictGroup.countContacts() != countContacts() + || dictGroup.countSubgroups() != countSubgroups()) + return false; + + //traverse child contacts + Iterator theirContacts = dictGroup.contacts(); + + while(theirContacts.hasNext()) + { + ContactDictImpl theirContact + = (ContactDictImpl)theirContacts.next(); + + ContactDictImpl ourContact + = (ContactDictImpl)getContact(theirContact.getAddress()); + + if(ourContact == null + || !ourContact.equals(theirContact)) + return false; + } + + //traverse subgroups + Iterator theirSubgroups = dictGroup.subgroups(); + + while(theirSubgroups.hasNext()) + { + ContactGroupDictImpl theirSubgroup + = (ContactGroupDictImpl)theirSubgroups.next(); + + ContactGroupDictImpl ourSubgroup + = (ContactGroupDictImpl)getGroup( + theirSubgroup.getGroupName()); + + if(ourSubgroup == null + || !ourSubgroup.equals(theirSubgroup)) + return false; + } + + return true; + } +} + diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictAccountID.java b/src/net/java/sip/communicator/impl/protocol/dict/DictAccountID.java new file mode 100644 index 0000000..6f823f5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/DictAccountID.java @@ -0,0 +1,33 @@ +/* + * 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.dict; + +import net.java.sip.communicator.service.protocol.*; +import java.util.Map; + +/** + * The Dict implementation of a sip-communicator account id. + * @author LITZELMANN Cédric + * @author ROTH Damien + */ +public class DictAccountID + 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. + */ + DictAccountID(String userID, Map accountProperties) + { + super( userID, + accountProperties, + "Dict", + "dict.org"); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java b/src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java new file mode 100644 index 0000000..455fb63 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java @@ -0,0 +1,108 @@ +/* + * 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.dict; + +import org.osgi.framework.*; +import net.java.sip.communicator.util.*; +import java.util.*; +import net.java.sip.communicator.service.protocol.*; + +/** + * Loads the Dict provider factory and registers its services in the OSGI + * bundle context. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class DictActivator + implements BundleActivator +{ + private static final Logger logger = Logger.getLogger(DictActivator.class); + + /** + * The currently valid bundle context. + */ + private static BundleContext bundleContext = null; + + private ServiceRegistration dictPpFactoryServReg = null; + private static ProtocolProviderFactoryDictImpl + dictProviderFactory = null; + + /** + * Called when this bundle is started. In here we'll export the + * dict 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 + { + bundleContext = context; + + Hashtable hashtable = new Hashtable(); + hashtable.put(ProtocolProviderFactory.PROTOCOL, ProtocolNames.DICT); + + dictProviderFactory = new ProtocolProviderFactoryDictImpl(); + + //load all stored Dict accounts. + dictProviderFactory.loadStoredAccounts(); + + //reg the dict provider factory. + dictPpFactoryServReg = context.registerService( + ProtocolProviderFactory.class.getName(), + dictProviderFactory, + hashtable); + + logger.info("DICT 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; + } + + /** + * 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.dictProviderFactory.stop(); + dictPpFactoryServReg.unregister(); + + logger.info("DICT protocol implementation [STOPPED]."); + } + + /** + * Retrurns a reference to the protocol provider factory that we have + * registered. + * @return a reference to the <tt>ProtocolProviderFactoryDictImpl</tt> + * instance that we have registered from this package. + */ + public static ProtocolProviderFactoryDictImpl getProtocolProviderFactory() + { + return dictProviderFactory; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictAdapter.java b/src/net/java/sip/communicator/impl/protocol/dict/DictAdapter.java new file mode 100644 index 0000000..433c423 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/DictAdapter.java @@ -0,0 +1,605 @@ +/* + * 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.dict; + +import java.io.*; +import java.net.*; +import java.util.*; + +/** + * Layer abstraction of a dict server + * + * @author LITZELMANN Cédric + * @author ROTH Damien + */ +public class DictAdapter +{ + /** + * The host name of the server: i.e. "dict.org" + */ + private String host; + + /** + * The port used by the server. The default one for the DICT protocol is + * 2628. + */ + private int port; + + /** + * The name of the strategy used for searching words with command MATCH: + * i.e. the strategie can de "prefix", "suffix", "soundex", "levenshtein", etc. + */ + private String strategy; + + /** + * A string representation used to identify the client to the serveur. In + * our case we will use the "SIP Communicator" string for the client name. + */ + private String clientName = ""; + + // Status + /** + * The socket used to connect to the DICT server. + */ + private Socket socket; + + /** + * A output stream piped to the socket in order to send command to the server. + */ + private PrintWriter out; + + /** + * A input stream piped to the socket in order to receive messages from the server. + */ + private BufferedReader in; + + /** + * A boolean telling if we are currently connected to the DICT server. + */ + private boolean connected; + + /** + * The list of all the databases hosted by the server. Each database + * correspond to a dictionnary. + */ + Vector<String> databasesList; + + /** + * Initialize a basic instance with predefined settings + */ + public DictAdapter() + { + this.host = "dict.org"; + this.port = 2628; + this.strategy = "prefix"; + this.connected = false; + this.socket = null; + this.out = null; + this.in = null; + } + + /** + * Initialize a basic instance and set th host + * @param host Host + */ + public DictAdapter(String host) + { + this.host = host; + this.port = 2628; + this.strategy = "prefix"; + this.connected = false; + this.socket = null; + this.out = null; + this.in = null; + } + + /** + * Initialize an instance and set the host and the port + * @param host Host + * @param port Port + */ + public DictAdapter(String host, int port) + { + this.host = host; + this.port = port; + this.strategy = "prefix"; + this.connected = false; + this.socket = null; + this.out = null; + this.in = null; + } + + /** + * Initialize an instance and set the host, port and strategy + * @param host Host + * @param port Port + * @param strategy Match strategy + */ + public DictAdapter(String host, int port, String strategy) + { + this.host = host; + this.port = port; + this.strategy = strategy; + this.connected = false; + this.socket = null; + this.out = null; + this.in = null; + } + + + /** + * Establish a connexion to the dict server + * @throws Exception + * @return DictResultset containing the error - null otherwise + */ + private void connect() throws Exception + { + String fromServer; + + if (this.isConnected()) + { + return; + } + + try + { + this.socket = new Socket(this.host, this.port); + this.out = new PrintWriter(new OutputStreamWriter(this.socket.getOutputStream(), + "UTF-8"), true); + this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), + "UTF-8")); + + fromServer = this.in.readLine(); // Server banner + + if (fromServer.startsWith("220")) + { // 220 = connect ok + this.connected = true; + this.client("SIP Communicator"); + return; + } + else + { + throw new DictException(fromServer.substring(0, 3)); + } + } + catch(UnknownHostException uhe) + { + throw new DictException(uhe); + } + catch(IOException ioe) + { + throw new DictException(ioe); + } + } + + /** + * Close the actual connexion + * @throws Exception + */ + public void close() throws Exception + { + String fromServer; + boolean quit = false; + + if (!this.isConnected()) + { + return; + } + + try + { + this.out.println("QUIT"); + + // Clean the socket buffer + while (quit == false && (fromServer = this.in.readLine()) != null) + { + if (fromServer.startsWith("221")) + { // Quit response + quit = true; + } + } + + this.out.close(); + this.in.close(); + this.socket.close(); + + this.connected = false; + } + catch (IOException ioe) + { + throw new DictException(ioe); + } + } + + /** + * Get the database list from the server + * @throws Exception + * @return a DictResultset containing the database list - otherwise the error code + */ + public DictResultset showDB() throws Exception + { + String fromServer; + boolean quit = false; + DictResultset result = new DictResultset(); + this.connect(); + + try + { + fromServer = this.query("SHOW DB"); + + if (fromServer.startsWith("110")) + { // OK - getting responses from the server + result.newResultset(); + while (quit == false && (fromServer = this.in.readLine()) != null) + { + if (fromServer.startsWith("250")) + { + quit = true; + } + else if (!fromServer.equals(".")) + { + result.addResult(fromServer); + } + } + } + else + { + throw new DictException(fromServer.substring(0,3)); + } + } + catch (IOException ioe) + { + throw new DictException(ioe); + } + + return result; + } + + /** + * Get the strategies allowed by the server for the MATCH command + * @throws Exception + * @return a DictResultset containing the database list - otherwise the error code + */ + public DictResultset showStrat() throws Exception + { + String fromServer; + boolean quit = false; + DictResultset result = new DictResultset(); + this.connect(); + + try + { + fromServer = this.query("SHOW STRAT"); + + if (fromServer.startsWith("111")) + { // OK - getting responses from the server + result.newResultset(); + while (quit == false && (fromServer = this.in.readLine()) != null) + { + if (fromServer.startsWith("250")) + { + quit = true; + } + else if (!fromServer.equals(".")) + { + result.addResult(fromServer); + } + } + } + else + { + throw new DictException(fromServer.substring(0,3)); + } + } + catch (IOException ioe) + { + throw new DictException(ioe); + } + + return result; + } + + + /** + * Get the definition of a word + * @param database the database in which the word will be searched + * @param word the search word + * @throws Exception + * @return a DictResultset containing the database list - otherwise the error code + */ + public DictResultset define(String database, String word) throws Exception + { + String fromServer; + boolean quit = false; + DictResultset result = new DictResultset(); + String[] test; + this.connect(); + + try + { + fromServer = this.query("DEFINE " + database + " " + word); + + if (fromServer.startsWith("150")) + { + while (quit == false && (fromServer = this.in.readLine()) != null) + { + if (fromServer.startsWith("151")) + { // First line - Contains the DB Name + test = fromServer.split(" ", 4); + result.newResultset(test[3].substring(1, test[3].length() - 1)); + continue; + } + else if (fromServer.startsWith("250")) + { // End of the request + quit = true; + } + else if (!fromServer.equals(".")) + { + result.addResult(fromServer); + } + } + } + else + { + throw new DictException(fromServer.substring(0,3)); + } + } + catch (IOException ioe) + { + throw new DictException(ioe); + } + + return result; + } + + /** + * Get words that match with a strategie form a word with the stored strategy + * @param database The database in which the words will be searched + * @param word The base word + * @return a DictResultset containing the words list - otherwise throw an exception + * @throws Exception + */ + public DictResultset match(String database, String word) throws Exception + { + return this.match(database, this.strategy, word); + } + + /** + * Get words that match with a strategie from a word + * @param database the database in which the words will be searched + * @param strat the strategies used + * @param word the base word + * @throws Exception + * @return a DictResultset containing the words list - otherwise the error code + */ + public DictResultset match(String database, String strat, String word) throws Exception + { + String fromServer; + boolean quit = false; + DictResultset result = new DictResultset(); + this.connect(); + + try + { + fromServer = this.query("MATCH " + database + " " + strat + " " + word); + + if (fromServer.startsWith("152")) + { + result.newResultset(); + while (quit == false && (fromServer = this.in.readLine()) != null) + { + if (fromServer.startsWith("250")) + { + quit = true; + } + else if (!fromServer.equals(".")) + { + result.addResult(fromServer); + } + } + } + else + { + throw new DictException(fromServer.substring(0,3)); + } + } + catch (IOException ioe) + { + throw new DictException(ioe); + } + + return result; + } + + /** + * Provide information to the server about the clientname, for logging and statistical purposes + * @param clientname Client name + * @throws Exception + */ + public void client(String clientname) throws Exception + { + String fromServer; + this.connect(); + + fromServer = this.query("CLIENT " + clientname); + + // 250 code is the only possible answer + if (!fromServer.startsWith("250")) + { + throw new DictException(fromServer.substring(0, 3)); + } + } + + /** + * Set the host + * @param newHost host address + */ + public void setHost(String newHost) throws Exception + { + if (isUrl(newHost)) + { + this.host = newHost; + } + else + { + throw new DictException(900, "Host URL is incorrect"); + } + } + + /** + * Set the host port + * @param newPort Port + */ + public void setPort(int newPort) + { + this.port = newPort; + } + + /** + * Set the strategy + * @param newStrat Strategy + */ + public void setStrategy(String newStrat) + { + this.strategy = newStrat; + } + + /** + * Set the client name which is communicated to the server + * @param cn Client name + */ + public void setClientName(String cn) + { + this.clientName = cn; + } + + /** + * Return the host + * @return return the host + */ + public String getHost() + { + return this.host; + } + + /** + * Return the port + * @return return the port + */ + public int getPort() + { + return this.port; + } + + /** + * Return the strategy + * @return return the strategy + */ + public String getStrategy() + { + return this.strategy; + } + + /** + * Return the client name + * @return return the client name + */ + public String getClientName() + { + return this.clientName; + } + + /** + * Gets the database's list from the server + * @return List of the databases + * @throws Exception + */ + public Vector<String> getDatabases() throws Exception + { + if (this.databasesList == null) + { + DictResultset drs = this.showDB(); + DictResult list = drs.getResultset(0); + + this.databasesList = new Vector<String>(); + + while(list.hasNext()) + { + this.databasesList.add(list.next()); + } + } + return this.databasesList; + } + + /** + * Check if we are connected to the server + * @return true if we are connected - false otherwise + */ + public boolean isConnected() + { + return this.connected; + } + + /** + * Check if the URL is correct and a server exists + * @param url an Url + * @return true if everything is ok - false otherwise + */ + public static boolean isUrl(String url) + { + boolean ok; + if (url == null) + { + return false; + } + + try + { + InetAddress.getByName(url); + ok = true; + } + catch (UnknownHostException uhex) + { + ok = false; + } + + return ok; + } + + /** + * Executes a query and deals with the automatic deconnexion + * @param query A query to send to the server + * @return The first ligne of the response from the server + * @throws Exception IOException and DictException + */ + private String query(String query) throws Exception + { + String result = null; + + this.out.println(query); + result = in.readLine(); + + if (result == null) + { + // The connexion may be close, reconnexion + this.connected = false; + this.connect(); + + this.out.println(query); + result = in.readLine(); + + if (result == null) + { + // If result is still equal to null, the server is unavailable + // We send the appropriate exception + throw new DictException(420); + } + } + + return result; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictException.java b/src/net/java/sip/communicator/impl/protocol/dict/DictException.java new file mode 100644 index 0000000..7792365 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/DictException.java @@ -0,0 +1,287 @@ +/* + * 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.dict; + +/** + * Exception class managing the basics dict server errors. + * + * @author LITZELMANN Cédric + * @author ROTH Damien + */ +public class DictException + extends Exception +{ + // Error number + private int error; + + // Error message + private String errorMessage; + + /** + * Create an exception from the dict error code + * @param error Error code returned by the server + */ + public DictException(int error) + { + this.error = error; + } + + /** + * Create an exception with a custom message + * @param error Error code returned by the server + * @param message Custom message + */ + public DictException(int error, String message) + { + this.error = error; + this.errorMessage = message; + } + + /** + * Same as the first constructor but with a string (converted to an int) + * @param error Error code returned by the server + */ + public DictException(String error) + { + this.error = Integer.parseInt(error); + } + + /** + * Create an exception from a java exception + * @param e Java Exception + */ + public DictException(Exception e) + { + String className = e.getClass().toString(); + + if (className.endsWith("IOException")) + { + this.error = 901; + this.errorMessage = "IOException"; + } + else if (className.endsWith("UnknownHostException")) + { + this.error = 902; + this.errorMessage = "UnknownHostException"; + } + else if (className.endsWith("SecurityException")) + { + this.error = 903; + this.errorMessage = "SecurityException"; + } + else if (className.endsWith("SocketTimeoutException")) + { + this.error = 904; + this.errorMessage = "SocketTimeoutException"; + } + else + { + this.error = 900; + this.errorMessage = "Unknown error [" + className + "]"; + } + + this.errorMessage += ": " + e.getMessage(); + } + + /** + * Return the error code + * @return the error code + */ + public int getErrorCode() + { + return this.error; + } + + /** + * Return the error message + * @return the error message + */ + public String getMessage() + { + return this.errorMessage; + } + + /** + * Get an explanation of the error + * @return Returns an explanation corresponding to the current error + * (this.error). + */ + public String getErrorMessage() + { + String result; + switch(this.error) + { + case 110 : + result = "n databases present"; + break; + case 111 : + result = "n strategies available"; + break; + case 112 : + result = "database information follows"; + break; + case 113 : + result = "help text follows"; + break; + case 114 : + result = "server information follows"; + break; + case 130 : + result = "challenge follows"; + break; + case 150 : + result = "n definitions retrieved"; + break; + case 151 : + result = "word database name"; + break; + case 152 : + result = "n matches found"; + break; + case 210 : + result = "optional timing"; + break; + case 220 : + result = "Connexion OK"; + break; + case 221 : + result = "Closing Connection"; + break; + case 230 : + result = "Authentification successful"; + break; + case 250 : + result = "Ok"; + break; + case 330 : + result = "send response"; + break; + case 420 : + result = "Server temporarily unavailable"; + break; + case 421 : + result = "Server shutting down at operator request"; + break; + case 500 : + result = "Syntax error, command not recognized"; + break; + case 501 : + result = "Syntax error, illegal parameters"; + break; + case 502 : + result = "Command not implemented"; + break; + case 503 : + result = "Command parameter not implemented"; + break; + case 530 : + result = "Access denied"; + break; + case 531 : + result = "Access denied, use SHOW INFO for server information"; + break; + case 532 : + result = "Access denied, unknown mechanism"; + break; + case 550 : + result = "Invalid database, use SHOW DB for list of databases"; + break; + case 551 : + result = "Invalid strategy, use SHOW STRAT for a list of strategies"; + break; + case 552 : + result = "No match"; + break; + case 554 : + result = "No databases present"; + break; + case 555 : + result = "No strategies available"; + break; + default : + if (error >= 900) + { + result = this.errorMessage; + } + else + { + result = this.errorGen(error); + } + } + + return result; + } + + /** + * Get informations about unknowns errors + * @param err Error number + * @return Error definition + */ + private String errorGen(int err) + { + String error_type = Integer.toString(err); + + String result = new String(); + + if(error_type.startsWith("1")) + {// test on digit one + result = "Positive Preliminary reply : "; + } + else if(error_type.startsWith("2")) + { + result = "Positive Completion reply : " ; + } + else if(error_type.startsWith("3")) + { + result = "Positive Intermediate reply : "; + } + else if(error_type.startsWith("4")) + { + result = "Transient Negative Completion reply : "; + } + else if(error_type.startsWith("5")) + { + result = "Permanent Negative Completion reply : "; + } + else + { + return "Unknown error"; + } + + //test on digit two + if(error_type.charAt(1) == '0') + { + result += "Syntax"; + } + else if ( error_type.charAt(1) == '1') + { + result += "Information"; + } + else if ( error_type.charAt(1) == '2') + { + result += "Connections"; + } + else if ( error_type.charAt(1) == '3') + { + result += "Authentication"; + } + else if ( error_type.charAt(1) == '4') + { + result += "Unspecified as yet"; + } + else if ( error_type.charAt(1) == '5') + { + result += "DICT System"; + } + else if ( error_type.charAt(1) == '8') + { + result += "Nonstandard (private implementation) extensions"; + } + + return result; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictRegistry.java b/src/net/java/sip/communicator/impl/protocol/dict/DictRegistry.java new file mode 100644 index 0000000..eca70b7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/DictRegistry.java @@ -0,0 +1,64 @@ +/* + * 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.dict; + +import java.util.*; + +/** + * Static registry storing the connexions to dict servers + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class DictRegistry +{ + private static HashMap<String, DictAdapter> adapters = new HashMap<String,DictAdapter>(); + + /** + * Checks if an adapter associated with the given key is stored in the regitry + * @param key Key to the adapter + * @return true, if an adapter exists - false otherwise + */ + public static boolean has(String key) + { + return adapters.containsKey(key); + } + + /** + * Stores a new adapter in the registry + * @param key Key + * @param value DictAdapter class + */ + public static void put(String key, DictAdapter value) + { + adapters.put(key, value); + } + + /** + * Returns the adapter associated with the given key + * @param key Key + * @return the adapter associated with the given key - null otherwise + */ + public static DictAdapter get(String key) + { + if (DictRegistry.has(key)) + { + return adapters.get(key); + } + return null; + } + + /** + * Removes the adapter associated with the given key from the registry + * @param key + */ + public static void remove(String key) + { + if (DictRegistry.has(key)) { + adapters.remove(key); + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictResult.java b/src/net/java/sip/communicator/impl/protocol/dict/DictResult.java new file mode 100644 index 0000000..464d915 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/DictResult.java @@ -0,0 +1,97 @@ +/*
+ * 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.dict;
+
+import java.util.*;
+
+/**
+ * Representation of the results of a response to a query
+ *
+ * @author ROTH Damien
+ * @author LITZEMANN Cédric
+ */
+public class DictResult
+ implements Iterator<String>
+{
+ private String databaseName;
+ private int index;
+ private ArrayList<String> data;
+
+ /**
+ * Basic construct initializing the result
+ */
+ public DictResult()
+ {
+ this.index = 0;
+ this.data = new ArrayList<String>();
+ this.databaseName = "";
+ }
+
+ /**
+ * Initialize the result and save the database name
+ * @param dbn Database name
+ */
+ public DictResult(String dbn)
+ {
+ this.index = 0;
+ this.data = new ArrayList<String>();
+ this.databaseName = dbn;
+ }
+
+ /**
+ * Add a result
+ * @param s result
+ */
+ public void add(String s)
+ {
+ this.data.add(s);
+ }
+
+ /**
+ * From the Iterator implementation, return the next part of the result
+ * @return the next part of the result
+ */
+ public String next()
+ {
+ return (String) this.data.get(this.index++);
+ }
+
+ /**
+ * From the Iterator implementation, return true if the iteration has more elements
+ * @return true if the iteration has more elements - false otherwise
+ */
+ public boolean hasNext()
+ {
+ return (this.index < this.data.size());
+ }
+
+ /**
+ * From the Iterator implementation but unsupported
+ */
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Set the database name
+ * @param dbn Database name
+ */
+ public void setDatabaseName(String dbn)
+ {
+ this.databaseName = dbn;
+ }
+
+ /**
+ * Return the database name
+ * @return the database name
+ */
+ public String getDatabaseName()
+ {
+ return this.databaseName;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictResultset.java b/src/net/java/sip/communicator/impl/protocol/dict/DictResultset.java new file mode 100644 index 0000000..3e4251c --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/DictResultset.java @@ -0,0 +1,107 @@ +/* + * 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.dict; + +import java.util.*; + +/** + * Class managing the results of a dict query + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class DictResultset +{ + /** + * The index number of the last resultset for this DictResultset. This + * parametre is set to "-1" if there is no resultset. + */ + private int cursor; + + /** + * The list containing all the resultsets for this DictResultset. + */ + private ArrayList<DictResult> data; + + /** + * Initialize a resultset list + */ + public DictResultset() + { + this.cursor = -1; + this.data = new ArrayList<DictResult>(); + } + + /** + * Create a new resultset + */ + public void newResultset() + { + this.cursor++; + this.data.add(new DictResult()); + } + + /** + * Create a new resultset and save the database name + * @param dbn Database name + */ + public void newResultset(String dbn) + { + this.cursor++; + this.data.add(new DictResult(dbn)); + } + + /** + * Set the database name for the current resultset + * @param dbn Database name + */ + public void setDatabaseName(String dbn) + { + this.data.get(this.cursor).setDatabaseName(dbn); + } + + /** + * Add a result in the current resultset + * @param res a result line from a dict query + */ + public void addResult(String res) + { + this.data.get(this.cursor).add(res); + } + + /** + * Return true if there is a resultset + * @return return true if there is a resultset - false otherwise + */ + public boolean hasResult() + { + return this.data.size() > 0; + } + + /** + * Return the resultset at the given index + * @param index Index of the wished resultset + * @return a DictResult - null otherwise + */ + public DictResult getResultset(int index) + { + if (index < this.data.size()) + { + return (DictResult) this.data.get(index); + } + return null; + } + + /** + * Return the number of resultsets + * @return the number of resultsets + */ + public int getNbResults() + { + return this.data.size(); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.java b/src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.java new file mode 100644 index 0000000..26d6ac6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.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.dict; + +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 Dict contact can fall into. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class DictStatusEnum + extends PresenceStatus +{ + private static final Logger logger + = Logger.getLogger(DictStatusEnum.class); + + /** + * Indicates an Offline status or status with 0 connectivity. + */ + public static final DictStatusEnum OFFLINE + = new DictStatusEnum( + 0 + , "Offline" + , loadIcon("resources/images/protocol/dict/dict-16x16.png")); + + /** + * The Online status. Indicate that the user is able and willing to + * communicate. + */ + public static final DictStatusEnum ONLINE + = new DictStatusEnum( + 65 + , "Online" + , loadIcon("resources/images/protocol/dict/dict-16x16.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 DictStatusEnum(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 = DictStatusEnum.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/dict/MessageDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/MessageDictImpl.java new file mode 100644 index 0000000..256e54d --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/MessageDictImpl.java @@ -0,0 +1,138 @@ +/* + * 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.dict; + +import net.java.sip.communicator.service.protocol.*; + +/** + * Very simple message implementation for the Dict protocol. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class MessageDictImpl + 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 MessageDictImpl(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/dict/OperationSetBasicInstantMessagingDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java new file mode 100644 index 0000000..4dc2848 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java @@ -0,0 +1,421 @@ +/* + * 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.dict; + +import java.util.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; + +import net.java.sip.communicator.util.*; + +/** + * Instant messaging functionalities for the Dict protocol. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class OperationSetBasicInstantMessagingDictImpl + implements OperationSetBasicInstantMessaging, + RegistrationStateChangeListener +{ + private static final Logger logger + = Logger.getLogger(OperationSetBasicInstantMessagingDictImpl.class); + + /** + * Currently registered message listeners. + */ + private Vector messageListeners = new Vector(); + + /** + * The currently valid persistent presence operation set. + */ + private OperationSetPersistentPresenceDictImpl opSetPersPresence = null; + + /** + * The protocol provider that created us. + */ + private ProtocolProviderServiceDictImpl parentProvider = null; + + /** + * 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>OperationSetPersistentPresenceDictImpl</tt> instance. + */ + public OperationSetBasicInstantMessagingDictImpl( + ProtocolProviderServiceDictImpl provider, + OperationSetPersistentPresenceDictImpl opSetPersPresence) + { + this.opSetPersPresence = opSetPersPresence; + this.parentProvider = provider; + + parentProvider.addRegistrationStateChangeListener(this); + } + + /** + * 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 MessageDictImpl(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 MessageDictImpl(messageText, DEFAULT_MIME_TYPE, + DEFAULT_MIME_ENCODING, null); + } + + /** + * Unregisters <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); + } + + /** + * 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 ContactDictImpl) ) + { + throw new IllegalArgumentException( + "The specified contact is not a Dict contact." + + to); + } + + // Display the queried word + fireMessageDelivered(message, to); + + this.submitDictQuery((ContactDictImpl) to, message); + } + + /** + * 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 whether the protocol provider (or the protocol itself) supports + * 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 false; + } + + /** + * Determines whether the protocol supports the supplied content type. + * + * @param contentType the type we want to check + * @return <tt>true</tt> if the protocol supports it and + * <tt>false</tt> otherwise. + */ + public boolean isContentTypeSupported(String contentType) + { + if(contentType.equals(DEFAULT_MIME_TYPE)) + return true; + else if(contentType.equals("text/html")) + return true; + else + return false; + } + + /** + * Returns the protocol provider that this operation set belongs to. + * + * @return a reference to the <tt>ProtocolProviderServiceDictImpl</tt> + * instance that this operation set belongs to. + */ + public ProtocolProviderServiceDictImpl getParentProvider() + { + return this.parentProvider; + } + + /** + * Returns a reference to the presence operation set instance used by our + * source provider. + * + * @return a reference to the <tt>OperationSetPersistentPresenceDictImpl</tt> + * instance used by this provider. + */ + public OperationSetPersistentPresenceDictImpl getOpSetPersPresence() + { + return this.opSetPersPresence; + } + + /** + * The method is called by the ProtocolProvider whenever a change in the + * registration state of the corresponding provider has occurred. + * + * @param evt ProviderStatusChangeEvent the event describing the status + * change. + */ + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + + } + + + /** + * Create, execute and display a query to a dictionary (ContactDictImpl) + * + * @param dictContact the contact containing the database name + * @param message the message containing the word + */ + private void submitDictQuery(ContactDictImpl dictContact, Message message) + { + Message msg = this.createMessage(""); + + String database = dictContact.getContactID(); + DictAdapter dictAdapter = dictContact.getDictAdapter(); + boolean doMatch = false; + DictResultset fctResult; + + String word; + + // Formatting the query message, if the word as one or more spaces we + // put it between quotes to prevent errors + word = message.getContent().replace("\"", "").trim(); + if (word.indexOf(' ') > 0) + { + word = "\"" + word + "\""; + } + + // Try to get the definition of the work + try + { + fctResult = dictAdapter.define(database, word); + msg = this.createMessage(this.retrieveDefine(fctResult, word)); + } + catch(DictException dex) + { + if (dex.getErrorCode() == 552) + { // No word found, we are going to try the match command + doMatch = true; + } + else + { // Otherwise we display the error returned by the server + msg = this.createMessage(dex.getErrorMessage()); + } + } + catch(Exception ex) + { + logger.error("Failed to retrieve Definition. Error was: " + + ex.getMessage() + , ex); + } + + if (doMatch) + { + // Trying the match command + try + { + fctResult = dictAdapter.match(database, word); + msg = this.createMessage(this.retrieveMatch(fctResult, word)); + } + catch(DictException dex) + { + msg = this.createMessage(dex.getErrorMessage()); + } + catch(Exception ex) + { + logger.error("Failed to retrieve Match. Error was: " + + ex.getMessage() + , ex); + } + } + + // Send message + fireMessageReceived(msg, dictContact); + } + + /** + * Generate the display of the results of the Define command + * + * @param data the result of the Define command + * @param word the queried word + * @return the formatted result + */ + private String retrieveDefine(DictResultset data, String word) + { + String result; + DictResult resultData; + + result = data.getNbResults() + " definitions found for \"" + word + "\""; + + for (int i=0; i<data.getNbResults(); i++) + { + resultData = data.getResultset(i); + + result += "\n"; + + while (resultData.hasNext()) + { + result += "\n" + resultData.next(); + } + } + + return result; + } + + /** + * Generate the display of the results of the Match command + * + * @param data the result of the Match command + * @param word the queried word + * @return the formatted result + */ + private String retrieveMatch(DictResultset data, String word) + { + String result = ""; + String temp; + DictResult resultData; + boolean isStart = true; + + result = "No definitions found for \""+ word +"\", perhaps you mean:\n"; + + for (int i=0; i<data.getNbResults(); i++) + { + resultData = data.getResultset(i); + + while(resultData.hasNext()) + { + temp = resultData.next(); + + if (isStart) + { + isStart = false; + } + else + { + result += ", "; + } + + // Return format : dictCode "match word" + temp = (temp.split(" ", 2))[1]; + + if (temp.indexOf(" ") == -1) + { + temp = temp.substring(1, temp.length() -1); + } + + result += temp; + } + } + + return result; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetDirectoryDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetDirectoryDictImpl.java new file mode 100644 index 0000000..647ecc6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetDirectoryDictImpl.java @@ -0,0 +1,58 @@ +package net.java.sip.communicator.impl.protocol.dict; + +import java.util.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; + +public class OperationSetDirectoryDictImpl + implements OperationSetDirectory +{ + private static final Logger logger + = Logger.getLogger(OperationSetDirectoryDictImpl.class); + + /** + * The protocol provider that created us. + */ + private ProtocolProviderServiceDictImpl parentProvider = null; + + /** + * 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. + */ + public OperationSetDirectoryDictImpl(ProtocolProviderServiceDictImpl provider) + { + this.parentProvider = provider; + } + + /** + * Returns directory entries according to a search and a return type + * + * @param search the search, may be an empty String + * @param returnType entries type + * @return an HashTable which links ids with visual infos - NULL otherwise + * @throws Exception Sends a exception if the host isn't reachable or if there isn't any dictionary + */ + public Hashtable<String,String> getEntries(String search, String returnType) throws Exception + { + DictAdapter adapter = this.parentProvider.getDictAdapter(); + Hashtable<String, String> result = null; + + String temp[]; + + result = new Hashtable<String,String>(); + Vector<String> dlist = adapter.getDatabases(); + for (int i=0; i<dlist.size(); i++) + { + temp = dlist.get(i).split(" ", 2); + temp[1] = temp[1].replace("\"", ""); + + result.put(temp[0], temp[1]); + } + + return result; + } +} + diff --git a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java new file mode 100644 index 0000000..0bbdfba --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java @@ -0,0 +1,1298 @@ +/* + * 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.dict; + +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 Dict 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 ROTH Damien + * @author LITZELMANN Cédric + */ +public class OperationSetPersistentPresenceDictImpl + implements OperationSetPersistentPresence +{ + private static final Logger logger = + Logger.getLogger(OperationSetPersistentPresenceDictImpl.class); + /** + */ + 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 dict contact list. + */ + private ContactGroupDictImpl contactListRoot = null; + + /** + * The provider that created us. + */ + private ProtocolProviderServiceDictImpl parentProvider = null; + + /** + * The currently active status message. + */ + private String statusMessage = "Default Status Message"; + + /** + * Our presence status. + */ + private PresenceStatus presenceStatus = DictStatusEnum.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 ProtocolProviderServiceDictImpl instance that + * created us. + */ + public OperationSetPersistentPresenceDictImpl( + ProtocolProviderServiceDictImpl provider) + { + this.parentProvider = provider; + contactListRoot = new ContactGroupDictImpl("RootGroup", provider); + + //add our unregistration listener + parentProvider.addRegistrationStateChangeListener( + new UnregistrationListener()); + } + + /** + * Dict 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(ContactDictImpl 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); + } + } + + + /** + * 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(ContactDictImpl 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(ContactGroupDictImpl source, + int eventID) + { + ServerStoredGroupEvent evt = new ServerStoredGroupEvent( + source, eventID, (ContactGroupDictImpl)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); + } + } + + /** + * Dict 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); + } + } + + /** + * Dict 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) + { + ContactGroupDictImpl newGroup + = new ContactGroupDictImpl(groupName, parentProvider); + + ((ContactGroupDictImpl)parent).addSubgroup(newGroup); + + this.fireServerStoredGroupEvent( + newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT); + } + + /** + * A Dict Provider method to use for fast filling of a contact list. + * + * @param contactGroup the group to add + */ + public void addDictGroup(ContactGroupDictImpl contactGroup) + { + contactListRoot.addSubgroup(contactGroup); + } + + /** + * A Dict 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 addDictGroupAndFireEvent( + ContactGroupDictImpl parent + , ContactGroupDictImpl 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 DictStatusEnum.supportedStatusSet(); + } + + /** + * 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) + { + ContactDictImpl dictContact + = (ContactDictImpl)contactToMove; + + ContactGroupDictImpl parentDictGroup + = findContactParent(dictContact); + + parentDictGroup.removeContact(dictContact); + + //if this is a volatile contact then we haven't really subscribed to + //them so we'd need to do so here + if(!dictContact.isPersistent()) + { + //first tell everyone that the volatile contact was removed + fireSubscriptionEvent(dictContact + , parentDictGroup + , SubscriptionEvent.SUBSCRIPTION_REMOVED); + + try + { + //now subscribe + this.subscribe(newParent, contactToMove.getAddress()); + + //now tell everyone that we've added the contact + fireSubscriptionEvent(dictContact + , newParent + , SubscriptionEvent.SUBSCRIPTION_CREATED); + } + catch (Exception ex) + { + logger.error("Failed to move contact " + + dictContact.getAddress() + , ex); + } + } + else + { + ( (ContactGroupDictImpl) newParent) + .addContact(dictContact); + + fireSubscriptionMovedEvent(contactToMove + , parentDictGroup + , 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()) + { + ContactDictImpl contact + = (ContactDictImpl) 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>ContactDictImpl</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( + ContactDictImpl 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()) + { + ContactDictImpl contact + = (ContactDictImpl)childContacts.next(); + + if(findProviderForDictUserID(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 dictGroup or null + * if no parent was found. + * @param dictGroup the group whose parent we're looking for. + * @return the ContactGroupDictImpl instance that dictGroup + * belongs to or null if no parent was found. + */ + public ContactGroupDictImpl findGroupParent( + ContactGroupDictImpl dictGroup) + { + return contactListRoot.findGroupParent(dictGroup); + } + + /** + * Returns the group that is parent of the specified dictContact or + * null if no parent was found. + * @param dictContact the contact whose parent we're looking for. + * @return the ContactGroupDictImpl instance that dictContact + * belongs to or null if no parent was found. + */ + public ContactGroupDictImpl findContactParent( + ContactDictImpl dictContact) + { + return (ContactGroupDictImpl)dictContact + .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 + { + ContactGroupDictImpl dictGroup + = (ContactGroupDictImpl)group; + + ContactGroupDictImpl parent = findGroupParent(dictGroup); + + if(parent == null){ + throw new IllegalArgumentException( + "group " + group + + " does not seem to belong to this protocol's contact list."); + } + + parent.removeSubGroup(dictGroup); + + this.fireServerStoredGroupEvent( + dictGroup, 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) + { + ((ContactGroupDictImpl)group).setGroupName(newName); + + this.fireServerStoredGroupEvent( + (ContactGroupDictImpl)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 + { + ContactDictImpl contact = new ContactDictImpl( + contactIdentifier + , parentProvider); + + ((ContactGroupDictImpl)parent).addContact(contact); + + fireSubscriptionEvent(contact, + parent, + SubscriptionEvent.SUBSCRIPTION_CREATED); + + //notify presence listeners for the status change. + fireContactPresenceStatusChangeEvent(contact + , parent + , DictStatusEnum.ONLINE); + } + + + /** + * 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 dict 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. + ProtocolProviderServiceDictImpl dictProvider + = this.findProviderForDictUserID(userID); + if (dictProvider != null) + { + OperationSetPersistentPresenceDictImpl opSetPersPresence + = (OperationSetPersistentPresenceDictImpl) + dictProvider.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 + { + ContactGroupDictImpl parentGroup + = (ContactGroupDictImpl)((ContactDictImpl)contact) + .getParentContactGroup(); + + parentGroup.removeContact((ContactDictImpl)contact); + + fireSubscriptionEvent((ContactDictImpl)contact, + ((ContactDictImpl)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) + { + + ContactDictImpl contact = new ContactDictImpl( + address + , parentProvider); + contact.setResolved(false); + + ( (ContactGroupDictImpl) 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. + contact.setResolved(true); + fireSubscriptionEvent(contact, parent, SubscriptionEvent.SUBSCRIPTION_RESOLVED); + + //since we are not a real protocol, we set the contact presence status + //ourselves + changePresenceStatusForContact( contact, getPresenceStatus()); + + return contact; + } + + /** + * Looks for a dict protocol provider registered for a user id matching + * <tt>dictUserID</tt>. + * + * @param dictUserID the ID of the Dict user whose corresponding + * protocol provider we'd like to find. + * @return ProtocolProviderServiceDictImpl a dict protocol + * provider registered for a user with id <tt>dictUserID</tt> or null + * if there is no such protocol provider. + */ + public ProtocolProviderServiceDictImpl + findProviderForDictUserID(String dictUserID) + { + BundleContext bc = DictActivator.getBundleContext(); + + String osgiQuery = "(&" + + "(" + ProtocolProviderFactory.PROTOCOL + + "=Dict)" + + "(" + ProtocolProviderFactory.USER_ID + + "=" + dictUserID + ")" + + ")"; + + 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 (ProtocolProviderServiceDictImpl)bc.getService(refs[0]); + } + + return null; + } + + /** + * Looks for dict 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 = DictActivator.getBundleContext(); + + String osgiQuery = + "(" + ProtocolProviderFactory.PROTOCOL + + "=Dict)"; + + 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++) + { + ProtocolProviderServiceDictImpl gibProvider + = (ProtocolProviderServiceDictImpl)bc.getService(refs[i]); + + OperationSetPersistentPresenceDictImpl opSetPersPresence + = (OperationSetPersistentPresenceDictImpl)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) + { + ContactGroupDictImpl newGroup + = new ContactGroupDictImpl( + ContactGroupDictImpl.createNameFromUID(groupUID) + , parentProvider); + newGroup.setResolved(false); + + //if parent is null then we're adding under root. + if(parentGroup == null) + parentGroup = getServerStoredContactListRoot(); + + ((ContactGroupDictImpl)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 dict 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()) + { + ContactGroupDictImpl group + = (ContactGroupDictImpl) groupsIter.next(); + + Iterator contactsIter = group.contacts(); + + while (contactsIter.hasNext()) + { + ContactDictImpl contact + = (ContactDictImpl) contactsIter.next(); + + PresenceStatus oldContactStatus + = contact.getPresenceStatus(); + + if (!oldContactStatus.isOnline()) + continue; + + contact.setPresenceStatus(DictStatusEnum.OFFLINE); + + 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 ContactGroupDictImpl getNonPersistentGroup() + { + for (int i = 0 + ; i < getServerStoredContactListRoot().countSubgroups() + ; i++) + { + ContactGroupDictImpl gr = + (ContactGroupDictImpl)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 ContactDictImpl createVolatileContact(String contactAddress) + { + //First create the new volatile contact; + ContactDictImpl newVolatileContact + = new ContactDictImpl(contactAddress + , this.parentProvider); + newVolatileContact.setPersistent(false); + + + //Check whether a volatile group already exists and if not create + //one + ContactGroupDictImpl theVolatileGroup = getNonPersistentGroup(); + + + //if the parent volatile group is null then we create it + if (theVolatileGroup == null) + { + List emptyBuddies = new LinkedList(); + theVolatileGroup = new ContactGroupDictImpl( + "NotInContactList" + , parentProvider); + theVolatileGroup.setResolved(false); + theVolatileGroup.setPersistent(false); + + 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/dict/ProtocolIconDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolIconDictImpl.java new file mode 100644 index 0000000..e906af1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolIconDictImpl.java @@ -0,0 +1,98 @@ +/* + * 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.dict; + +import java.io.*; +import java.util.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; + +/** + * Reperesents the Dict protocol icon. Implements the <tt>ProtocolIcon</tt> + * interface in order to provide a dict logo image in two different sizes. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class ProtocolIconDictImpl + implements ProtocolIcon +{ + private static Logger logger = Logger.getLogger(ProtocolIconDictImpl.class); + + /** + * A hash table containing the protocol icon in different sizes. + */ + private static Hashtable<String,byte[]> iconsTable = new Hashtable<String,byte[]>(); + static { + iconsTable.put(ProtocolIcon.ICON_SIZE_16x16, + loadIcon("resources/images/protocol/dict/dict-16x16.png")); + + iconsTable.put(ProtocolIcon.ICON_SIZE_64x64, + loadIcon("resources/images/protocol/dict/dict-64x64.png")); + } + + /** + * Implements the <tt>ProtocolIcon.getSupportedSizes()</tt> method. Returns + * an iterator to a set containing the supported icon sizes. + * @return Returns an iterator to a set containing the supported icon sizes + */ + public Iterator getSupportedSizes() + { + return iconsTable.keySet().iterator(); + } + + /** + * Returns TRUE if an icon with the given size is supported, FALSE otherwise. + * @param iconSize The size of the icon, that we want to know if it is + * supported. + * @return Returns true if the 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 + * @return Returns a byte[] containing the pixels of the icon for the given + * size. + */ + public byte[] getIcon(String iconSize) + { + return iconsTable.get(iconSize); + } + + /** + * Returns the icon image used to represent the protocol connecting state. + * @return Returns the icon image used to represent the protocol connecting state. + */ + public byte[] getConnectingIcon() + { + return iconsTable.get(ProtocolIcon.ICON_SIZE_16x16); + } + + /** + * 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 = ProtocolIconDictImpl.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/dict/ProtocolProviderFactoryDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderFactoryDictImpl.java new file mode 100644 index 0000000..11fc521 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderFactoryDictImpl.java @@ -0,0 +1,318 @@ +/* + * 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.dict; + +import java.util.*; + +import net.java.sip.communicator.impl.gui.i18n.*; +import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; + +import org.osgi.framework.*; + +/** + * The Dict protocol provider factory creates instances of the Dict + * protocol provider service. One Service instance corresponds to one account. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class ProtocolProviderFactoryDictImpl + extends ProtocolProviderFactory +{ + private static final Logger logger + = Logger.getLogger(ProtocolProviderFactoryDictImpl.class); + + /** + * The table that we store our accounts in. + */ + private Hashtable registeredAccounts = new Hashtable(); + + /** + * Name for the auto-create group + */ + private String groupName = "Dictionaries"; + + private BundleContext bundleContext; + + /** + * Creates an instance of the ProtocolProviderFactoryDictImpl. + */ + public ProtocolProviderFactoryDictImpl() + { + super(); + bundleContext = DictActivator.getBundleContext(); + } + + /** + * 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(DictActivator.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 = DictActivator.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 DictAccountID(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(DictActivator.getBundleContext(), accountID); + + accountID = loadAccount(accountProperties); + + // Creates the dict contact group + this.createGroup(); + + 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 = DictActivator.getBundleContext(); + + if(bundleContext == null) + { + throw new NullPointerException("The specified BundleContext was null"); + } + + String userIDStr = (String) accountProperties.get(USER_ID); + + AccountID accountID = new DictAccountID(userIDStr, accountProperties); + + //get a reference to the configuration service and register whatever + //properties we have in it. + + Hashtable properties = new Hashtable(); + properties.put(PROTOCOL, ProtocolNames.DICT); + properties.put(USER_ID, userIDStr); + + ProtocolProviderServiceDictImpl dictProtocolProvider + = new ProtocolProviderServiceDictImpl(); + + dictProtocolProvider.initialize(userIDStr, accountID); + + ServiceRegistration registration + = bundleContext.registerService( ProtocolProviderService.class.getName(), + dictProtocolProvider, + 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) DictActivator.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(DictActivator.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(DictActivator.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(DictActivator.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()); + } + } + + /** + * Creates a group for the dict contacts + */ + private void createGroup() + { + // Get MetaContactListService + ServiceReference mfcServiceRef = bundleContext + .getServiceReference(MetaContactListService.class.getName()); + + MetaContactListService mcl = (MetaContactListService) + bundleContext.getService(mfcServiceRef); + + try + { + mcl.createMetaContactGroup(mcl.getRoot(), groupName); + } + catch (MetaContactListException ex) + { + int errorCode = ex.getErrorCode(); + if (errorCode != MetaContactListException.CODE_GROUP_ALREADY_EXISTS_ERROR) + { + logger.error(ex); + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderServiceDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderServiceDictImpl.java new file mode 100644 index 0000000..c60e8e5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderServiceDictImpl.java @@ -0,0 +1,451 @@ +/* + * 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.dict; + +import java.util.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.version.*; +import net.java.sip.communicator.util.*; + +import org.osgi.framework.*; + +/** + * A Dict implementation of the ProtocolProviderService. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class ProtocolProviderServiceDictImpl + implements ProtocolProviderService +{ + private static final Logger logger + = Logger.getLogger(ProtocolProviderServiceDictImpl.class); + + /** + * The name of this protocol. + */ + public static final String DICT_PROTOCOL_NAME = "Dict"; + + /** + * 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 gibberish protocol. + */ + private ProtocolIconDictImpl dictIcon = new ProtocolIconDictImpl(); + + /** + * 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 Dict protocol provider. + */ + public ProtocolProviderServiceDictImpl() + { + logger.trace("Creating a Dict provider."); + } + + /** + * Initializes the service implementation, and puts it in a sate where it + * could interoperate with other services. It is strongly recomended that + * properties in this Map be mapped to property names as specified by + * <tt>AccountProperties</tt>. + * + * @param userID the user id of the gibberish 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 + OperationSetPersistentPresenceDictImpl persistentPresence = + new OperationSetPersistentPresenceDictImpl(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 + OperationSetBasicInstantMessagingDictImpl basicInstantMessaging + = new OperationSetBasicInstantMessagingDictImpl( + this + , (OperationSetPersistentPresenceDictImpl) + persistentPresence); + + supportedOperationSets.put( + OperationSetBasicInstantMessaging.class.getName(), + basicInstantMessaging); + + //initialize the typing notifications operation set + /*OperationSetTypingNotifications typingNotifications = + new OperationSetTypingNotificationsDictImpl( + 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); + } + + logger.trace("Done."); + } + + /** + * Retrieve the DictAdapter linked with the account. If there is no instance, one is created + * @return a DictAdapter instance + */ + public DictAdapter getDictAdapter() + { + String host = (String) this.accountID.getAccountProperties() + .get(ProtocolProviderFactory.SERVER_ADDRESS); + int port = Integer.parseInt((String) this.accountID.getAccountProperties() + .get(ProtocolProviderFactory.SERVER_PORT)); + String strategy = (String) this.accountID.getAccountProperties() + .get(ProtocolProviderFactory.STRATEGY); + + String key = this.accountID.getUserID(); + DictAdapter result = DictRegistry.get(key); + + if (!(result instanceof DictAdapter)) + { + result = new DictAdapter(host, port, strategy); + + + // Set the clientname from the current version + BundleContext bundleContext = DictActivator.getBundleContext(); + ServiceReference versionServRef = bundleContext + .getServiceReference(VersionService.class.getName()); + + VersionService versionService = (VersionService) bundleContext + .getService(versionServRef); + + result.setClientName(versionService.getCurrentVersion().toString()); + + // Store the DictAdapter + DictRegistry.put(key, result); + } + + return result; + } + + /** + * Close the DictAdapter linked with the account ID + */ + public void closeDictAdapter() + { + String key = this.accountID.getUserID(); + DictAdapter result = DictRegistry.get(key); + + if ((result instanceof DictAdapter)) + { + try + { + result.close(); + DictRegistry.remove(key); + } + catch (Exception ex) + { + logger.error(ex); + } + } + } + + /** + * 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 DICT_PROTOCOL_NAME; + } + + /** + * Returns the protocol display name. This is the name that would be used + * by the GUI to display the protocol name. + * + * @return a String containing the display name of the protocol this service + * is implementing + */ + public String getProtocolDisplayName() + { + return DICT_PROTOCOL_NAME; + } + + /** + * Returns the dict protocol icon. + * @return the dict protocol icon + */ + public ProtocolIcon getProtocolIcon() + { + return this.dictIcon; + } + + /** + * 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 + { + 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 Dict Protocol Provider for account " + + this.accountID.getUserID()); + this.closeDictAdapter(); + + 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); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/dict.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/dict/dict.provider.manifest.mf new file mode 100644 index 0000000..ff0b0d2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/dict/dict.provider.manifest.mf @@ -0,0 +1,13 @@ +Bundle-Activator: net.java.sip.communicator.impl.protocol.dict.DictActivator +Bundle-Name: Dict Protocol Provider +Bundle-Description: A bundle providing support for the Dict protocol. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.osgi.framework, + net.java.sip.communicator.service.contactlist, + 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, + net.java.sip.communicator.service.version diff --git a/src/net/java/sip/communicator/impl/version/NightlyBuildID.java b/src/net/java/sip/communicator/impl/version/NightlyBuildID.java index 32bba36..08d7931 100644 --- a/src/net/java/sip/communicator/impl/version/NightlyBuildID.java +++ b/src/net/java/sip/communicator/impl/version/NightlyBuildID.java @@ -13,5 +13,5 @@ package net.java.sip.communicator.impl.version; */ public class NightlyBuildID { - public static final String BUILD_ID="0.build.by.CVS"; + public static final String BUILD_ID="0.build.by.lucas"; } diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAccRegWizzActivator.java b/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAccRegWizzActivator.java new file mode 100644 index 0000000..7972841 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAccRegWizzActivator.java @@ -0,0 +1,136 @@ +/* + * 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.dictaccregwizz; + +import java.util.*; + +import net.java.sip.communicator.service.browserlauncher.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; + +import org.osgi.framework.*; + +/** + * Registers the <tt>DictAccountRegistrationWizard</tt> in the UI Service. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class DictAccRegWizzActivator implements BundleActivator +{ + public static BundleContext bundleContext; + + private static Logger logger = Logger.getLogger( + DictAccRegWizzActivator.class); + + private static BrowserLauncherService browserLauncherService; + + private static WizardContainer wizardContainer; + + private static DictAccountRegistrationWizard dictWizard; + + private static UIService uiService; + + /** + * Starts this bundle. + * + * @param bc The bundle context. + */ + public void start(BundleContext bc) throws Exception { + + bundleContext = bc; + + ServiceReference uiServiceRef = bundleContext + .getServiceReference(UIService.class.getName()); + + uiService = (UIService) bundleContext.getService(uiServiceRef); + + wizardContainer = uiService.getAccountRegWizardContainer(); + + dictWizard = new DictAccountRegistrationWizard(wizardContainer); + + //wizardContainer.addAccountRegistrationWizard(dictWizard); + Hashtable<String, String> containerFilter + = new Hashtable<String, String>(); + + containerFilter.put( + ProtocolProviderFactory.PROTOCOL, + ProtocolNames.DICT); + + bundleContext.registerService( + AccountRegistrationWizard.class.getName(), + dictWizard, + containerFilter); + } + + + /** + * Stops this bundle. + * + * @param bundleContext The bundle context (unused). + * + * @throws Exception Throws an execption from the + * "wizardContainer.removeAccountRegistrationWizard" method. + * + */ + public void stop(BundleContext bundleContext) throws Exception + { + //wizardContainer.removeAccountRegistrationWizard(dictWizard); + } + + /** + * Returns the <tt>ProtocolProviderFactory</tt> for the Dict protocol. + * @return the <tt>ProtocolProviderFactory</tt> for the Dict protocol + */ + public static ProtocolProviderFactory getDictProtocolProviderFactory() { + + ServiceReference[] serRefs = null; + + String osgiFilter = "(" + + ProtocolProviderFactory.PROTOCOL + + "="+ProtocolNames.DICT+")"; + + try { + serRefs = bundleContext.getServiceReferences( + ProtocolProviderFactory.class.getName(), osgiFilter); + } + catch (InvalidSyntaxException ex){ + logger.error("DictAccRegWizzActivator : " + ex); + } + + return (ProtocolProviderFactory) bundleContext.getService(serRefs[0]); + } + + /** + * Returns the <tt>BrowserLauncherService</tt> obtained from the bundle + * context. + * @return the <tt>BrowserLauncherService</tt> obtained from the bundle + * context + */ + public static BrowserLauncherService getBrowserLauncher() { + if (browserLauncherService == null) { + ServiceReference serviceReference = bundleContext + .getServiceReference(BrowserLauncherService.class.getName()); + + browserLauncherService = (BrowserLauncherService) bundleContext + .getService(serviceReference); + } + + return browserLauncherService; + } + + /** + * Returns the <tt>UIService</tt>. + * + * @return the <tt>UIService</tt> + */ + public static UIService getUIService() + { + return uiService; + } +} diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAccountRegistration.java b/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAccountRegistration.java new file mode 100644 index 0000000..5bda4e6 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAccountRegistration.java @@ -0,0 +1,123 @@ +/* + * 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.dictaccregwizz; + +/** + * The <tt>DictAccountRegistration</tt> is used to store all user input data + * through the <tt>DictAccountRegistrationWizard</tt>. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class DictAccountRegistration +{ + private String userID; + private String password; + + /** + * The hostname of the DICT server. + */ + private String host; + + /** + * The port of the DICT server. + */ + private int port; + + /** + * The code id of the strategie selected for the matching of words in dictionnaries. + */ + private String strategyCode; + + /** + * The real name of the strategie selected for the matching of words in dictionnaries. + */ + private String strategy; + + /** + * Returns the User ID of the dict registration account. + * @return the User ID of the dict registration account. + */ + public String getUserID() + { + return userID; + } + + /** + * Sets the password of the dict registration account. + * + * @param password the password of the dict registration account. + */ + public void setPassword(String password) + { + this.password = password; + } + + /** + * Returns the port of the dict registration account. + * @return the port of the dict registration account. + */ + public int getPort() { + return this.port; + } + + /** + * Sets the port of the dict registration account. + * @param port the port of the dict registration account. + */ + public void setPort(int port) { + this.port = port; + } + + /** + * Returns the host of the dict registration account. + * @return the host of the dict registration account. + */ + public String getHost() { + return this.host; + } + + /** + * Sets the host of the dict registration account. + * @param uin the host of the dict registration account. + */ + public void setHost(String host) { + this.host = host; + } + + /** + * Returns the strategy that will be used for this dict account. + * @return the strategy that will be used for this dict account. + */ + public String getStrategy() { + return this.strategy; + } + + /** + * Sets the strategy for this dict account. + * @param strategy the strategy for this dict account. + */ + public void setStrategy(String strategy) { + this.strategy = strategy; + } + + /** + * Returns the strategy code that will be used for this dict account. + * @return the strategy code that will be used for this dict account. + */ + public String getStrategyCode() { + return this.strategyCode; + } + + /** + * Sets the strategy code for this dict account. + * @param strategyCode the strategy code for this dict account. + */ + public void setStrategyCode(String strategyCode) { + this.strategyCode = strategyCode; + } +} diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAccountRegistrationWizard.java b/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAccountRegistrationWizard.java new file mode 100644 index 0000000..b2d2d5b --- /dev/null +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAccountRegistrationWizard.java @@ -0,0 +1,409 @@ +/* + * 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.dictaccregwizz; + +import java.awt.*; +import java.util.*; + +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.protocol.*; + +import org.osgi.framework.*; + +/** + * The <tt>DictAccountRegistrationWizard</tt> is an implementation of the + * <tt>AccountRegistrationWizard</tt> for the Dict protocol. It should allow + * the user to create and configure a new Dict account. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class DictAccountRegistrationWizard + implements AccountRegistrationWizard +{ + /** + * The reference to the first page of the wizard. + */ + private FirstWizardPage firstWizardPage; + + /** + * The registration of the DICT account. + */ + private DictAccountRegistration registration = new DictAccountRegistration(); + + /** + * The container of the wizard. + */ + private WizardContainer wizardContainer; + + /** + * The protocole provider. + */ + private ProtocolProviderService protocolProvider; + + /** + * Tells us if the is a modification wiazrd or not. + */ + private boolean isModification; + + /** + * Creates an instance of <tt>DictAccountRegistrationWizard</tt>. + * + * @param wizardContainer the wizard container, where this wizard is added + */ + public DictAccountRegistrationWizard(WizardContainer wizardContainer) + { + this.wizardContainer = wizardContainer; + } + + /** + * Implements the <code>AccountRegistrationWizard.getIcon</code> method. + * @return Returns the icon to be used for this wizard. + */ + public byte[] getIcon() + { + return Resources.getImage(Resources.DICT_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. + * @return Returns the protocol name for this wizard. + */ + public String getProtocolName() + { + return Resources.getString("protocolName"); + } + + /** + * Implements the <code>AccountRegistrationWizard.getProtocolDescription + * </code> method. + * @return Returns the description of the protocol for this wizard. + */ + public String getProtocolDescription() + { + return Resources.getString("protocolDescription"); + } + + /** + * Returns the set of pages contained in this wizard. + * + * @return Returns the set of pages contained in this wizard. + */ + public Iterator getPages() + { + ArrayList pages = new ArrayList(); + this.firstWizardPage = new FirstWizardPage(this); + pages.add(this.firstWizardPage); + return pages.iterator(); + } + + /** + * Returns the set of data that user has entered through this wizard. + * @return Returns the set of data that user has entered through this wizard. + */ + public Iterator getSummary() + { + LinkedHashMap<String, String> summaryTable = new LinkedHashMap<String, String>(); + + summaryTable.put("Host", registration.getHost()); + summaryTable.put("Port", String.valueOf(registration.getPort())); + summaryTable.put("Strategy", registration.getStrategy()); + + return summaryTable.entrySet().iterator(); + } + + /** + * Installs the account created through this wizard. + * + * @return The ProtocoleProviderService installed for the account. + */ + public ProtocolProviderService finish() + { + firstWizardPage = null; + ProtocolProviderFactory factory = + DictAccRegWizzActivator.getDictProtocolProviderFactory(); + + return this.installAccount(factory, registration.getHost(), + registration.getPort(), + registration.getStrategyCode()); + } + + /** + * Installs the account created through this wizard. + * @return ProtocolProviderService + */ + public ProtocolProviderService signin() + { + return signin(registration.getUserID(), null); + } + + /** + * Installs the account created through this wizard. + * @return ProtocolProviderService + */ + public ProtocolProviderService signin(String userName, String password) + { + firstWizardPage = null; + ProtocolProviderFactory factory + = DictAccRegWizzActivator.getDictProtocolProviderFactory(); + + /*return this.installAccount(factory, + userName);*/ + return this.installAccount(factory, registration.getHost(), + registration.getPort(), + registration.getStrategyCode()); + } + + /** + * Creates an account for the given user and password. + * + * @param providerFactory the ProtocolProviderFactory which will create the + * account. + * @param host The hostname of the DICT server. + * @param port The port used by the DICT server. + * @param strategy The strategy choosen for matching words in the + * dictionnaries. + * @return the <tt>ProtocolProviderService</tt> for the new account. + */ + public ProtocolProviderService installAccount( + ProtocolProviderFactory providerFactory, + String host, + int port, + String strategy) + { + Hashtable accountProperties = new Hashtable(); + + // Save host + accountProperties.put(ProtocolProviderFactory.SERVER_ADDRESS, host); + // Save port + accountProperties.put(ProtocolProviderFactory.SERVER_PORT, String.valueOf(port)); + // Save strategy + accountProperties.put(ProtocolProviderFactory.STRATEGY, strategy); + + if (isModification) + { + providerFactory.uninstallAccount(protocolProvider.getAccountID()); + this.protocolProvider = null; + this.isModification = false; + } + + try + { + String uid = this.generateUID(); + AccountID accountID = + providerFactory.installAccount(uid, accountProperties); + + ServiceReference serRef = + providerFactory.getProviderForAccount(accountID); + + protocolProvider = + (ProtocolProviderService) DictAccRegWizzActivator.bundleContext + .getService(serRef); + } + catch (IllegalArgumentException e) + { + DictAccRegWizzActivator.getUIService().getPopupDialog() + .showMessagePopupDialog(e.getMessage(), + Resources.getString("error"), + PopupDialog.ERROR_MESSAGE); + } + catch (IllegalStateException e) + { + DictAccRegWizzActivator.getUIService().getPopupDialog() + .showMessagePopupDialog(e.getMessage(), + Resources.getString("error"), + PopupDialog.ERROR_MESSAGE); + } + + return protocolProvider; + } + + /** + * Fills the UIN and Password fields in this panel with the data coming + * from the given protocolProvider. + * + * @param protocolProvider The <tt>ProtocolProviderService</tt> to load + * the data from. + */ + public void loadAccount(ProtocolProviderService protocolProvider) + { + this.isModification = true; + + this.protocolProvider = protocolProvider; + + this.registration = new DictAccountRegistration(); + + this.firstWizardPage.loadAccount(protocolProvider); + } + + /** + * Indicates if this wizard is opened for modification or for creating a + * new account. + * + * @return <code>true</code> if this wizard is opened for modification and + * <code>false</code> otherwise. + */ + public boolean isModification() + { + return isModification; + } + + /** + * Sets if this wizard is opened for modification or for creating a + * new account. + * + * @param b <code>True</code> if this wizard is opened for modification and + * <code>false</code> otherwise. + */ + public void setModification(boolean b) + { + this.isModification = b; + } + + /** + * Returns the wizard container, where all pages are added. + * + * @return the wizard container, where all pages are added + */ + public WizardContainer getWizardContainer() + { + return wizardContainer; + } + + /** + * Returns the registration object, which will store all the data through + * the wizard. + * + * @return the registration object, which will store all the data through + * the wizard + */ + public DictAccountRegistration getRegistration() + { + return registration; + } + + /** + * Returns the size of this wizard. + * @return the size of this wizard + */ + public Dimension getSize() + { + return new Dimension(300, 150); + } + + /** + * Returns the identifier of the page to show first in the wizard. + * @return the identifier of the page to show first in the wizard. + */ + public Object getFirstPageIdentifier() + { + return firstWizardPage.getIdentifier(); + } + + /** + * Returns the identifier of the page to show last in the wizard. + * @return the identifier of the page to show last in the wizard. + */ + public Object getLastPageIdentifier() + { + return firstWizardPage.getIdentifier(); + } + + /** + * Generate the UID for the acount + * @return the new UID + */ + private String generateUID() + { + String uid; + int nbAccounts = this.getNumberOfAccounts(); + String host = this.registration.getHost(); + int nbAccountsForHost = this.getNbAccountForHost(host); + + if (nbAccounts == 0 || (this.isModification() && nbAccounts == 1) || + nbAccountsForHost == 0 || (this.isModification() && nbAccountsForHost == 1)) + { + // We create the first account or we edit the onlyone + // Or we create the first account for this server or edit the onlyone + uid = host; + } + else + { + uid = host + ":" + this.registration.getPort(); + } + + return uid; + } + + /** + * Returns the number of accounts stored for the protocol + * @return the number of accounts stored for the protocol + */ + private int getNumberOfAccounts() + { + ProtocolProviderFactory factory = + DictAccRegWizzActivator.getDictProtocolProviderFactory(); + + return factory.getRegisteredAccounts().size(); + } + + /** + * Returns the number of account for a given host + * @param hostName the host + * @return the number of account for a given host + */ + private int getNbAccountForHost(String host) + { + ProtocolProviderFactory factory = + DictAccRegWizzActivator.getDictProtocolProviderFactory(); + + ArrayList registeredAccounts = factory.getRegisteredAccounts(); + int total = 0; + + for (int i = 0; i < registeredAccounts.size(); i++) + { + AccountID accountID = (AccountID) registeredAccounts.get(i); + + // The host is always stored at the start + if (accountID.getUserID().startsWith(host.toLowerCase())) + { + total++; + } + } + return total; + } + + /** + * Returns an example string, which should indicate to the user how the + * user name should look like. + * @return an example string, which should indicate to the user how the + * user name should look like. + */ + public String getUserNameExample() + { + return null; + } + + /** + * Disables the simple "Sign in" form. + */ + public boolean isSimpleFormEnabled() + { + return false; + } +} diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAdapter.java b/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAdapter.java new file mode 100644 index 0000000..e7329ab --- /dev/null +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/DictAdapter.java @@ -0,0 +1,183 @@ +/* + * 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.dictaccregwizz; + +import java.io.*; +import java.net.*; +import java.util.*; + +import net.java.sip.communicator.util.Logger; + +/** + * Copy of DictAdapter class to solve the import problem + * @author ROTH Damien + * @author LITZELMANN Cédric + */ + +public class DictAdapter +{ + private static Logger logger = Logger.getLogger(DictAdapter.class); + + /** + * The socket used to connect to the DICT server. + */ + private Socket socket = null; + + /** + * A output stream piped to the socket in order to send command to the server. + */ + private PrintWriter out = null; + + /** + * A input stream piped to the socket in order to receive messages from the server. + */ + private BufferedReader in = null; + + /** + * A boolean telling if we are currently connected to the DICT server. + */ + private boolean connected = false; + + /** + * Get the strategies allowed by the server for the MATCH command + * @return a HashMap containing the database list - otherwise null + */ + public ArrayList<String> getStrategies() + { + String fromServer; + boolean quit = false; + ArrayList<String> result = null; + + // Connexion + if (!this.connected) + { + // Not connected + return null; + } + + try + { + this.out.println("SHOW STRAT"); + fromServer = this.in.readLine(); + + if (fromServer.startsWith("111")) + { // OK - getting responses from the server + result = new ArrayList<String>(); + while (quit == false && (fromServer = this.in.readLine()) != null) + { + if (fromServer.startsWith("250")) + { + quit = true; + } + else if (!fromServer.equals(".")) + { + result.add(fromServer); + } + } + } + } + catch (IOException ioe) + { + logger.trace("Cannot get the strategies : " + ioe.getMessage()); + result = null; + } + return result; + } + + + /** + * Open a connection to the given host on the given port + * @param host The hostname of the server. + * @param port The port used by the server. + * @return true, if a connection is open - false otherwise + */ + public boolean connect(String host, int port) + { + this.connected = false; + String fromServer; + + try + { + this.socket = new Socket(host, port); + this.out = new PrintWriter(new OutputStreamWriter(this.socket.getOutputStream(), + "UTF-8"), true); + this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), + "UTF-8")); + + fromServer = this.in.readLine(); // Server banner + + if (fromServer.startsWith("220")) + { // 220 = connect ok + this.connected = true; + } + } + catch(Exception ex) + { // If an exception is throw == connexion impossible + logger.trace("Cannot establish a connexion to the server (" + + host + ":" + port + ")", ex); + } + + return this.connected; + } + + /** + * Close the connexion to the server + */ + public void close() + { + String fromServer; + + try + { + this.out.println("QUIT"); + + // Clean the socket buffer + while ((fromServer = this.in.readLine()) != null) + { + if (fromServer.startsWith("221")) + { // Quit response + break; + } + } + + this.out.close(); + this.in.close(); + this.socket.close(); + } + catch (IOException ioe) + { + logger.info("Cannot close the connextion to the server", ioe); + } + } + + /** + * Checks if the given url is correct and exists + * @param host The url that we have to test if it is correct and if it + * exists. + * @return true if the url exists - false otherwise + */ + public static boolean isUrl(String host) + { + boolean ok = false; + + if (host == null || host.length() == 0) + { + return false; + } + + // If an exception is throw, the host format isn't correct or isn't recheable + try + { + InetAddress.getByName(host); + ok = true; + } + catch (UnknownHostException uhex) + { + logger.trace("Test URL ("+host+") : " + uhex.getMessage(), uhex); + } + return ok; + } +} diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/dictaccregwizz/FirstWizardPage.java new file mode 100644 index 0000000..474bc6d --- /dev/null +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/FirstWizardPage.java @@ -0,0 +1,672 @@ +/* + * 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.dictaccregwizz; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; + +/** + * The <tt>FirstWizardPage</tt> is the page, where user could enter the host, + * port and the strategy of the account. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class FirstWizardPage + extends JPanel + implements WizardPage, DocumentListener, ActionListener +{ + private static Logger logger = Logger.getLogger(FirstWizardPage.class); + + public static final String FIRST_PAGE_IDENTIFIER = "FirstPageIdentifier"; + + private JPanel hostPortPanel = new JPanel(new BorderLayout(10, 10)); + + private JPanel labelsPanel = new JPanel(); + + private JPanel valuesPanel = new JPanel(); + + private JLabel hostLabel = new JLabel("Host"); + + private JPanel emptyPanel = new JPanel(); + + private JLabel hostExampleLabel = new JLabel("Ex: dict.org"); + + private JLabel portLabel = new JLabel("Port"); + + private JLabel existingAccountLabel = + new JLabel(Resources.getString("existingAccount")); + + private JTextField hostField = new JTextField(); + + private JTextField portField = new JTextField("2628"); + + + private JPanel strategyPanel = new JPanel(new BorderLayout(10, 10)); + + private JPanel strategyTitleBloc = new JPanel(new BorderLayout()); + + private JLabel strategyTitle = new JLabel(Resources.getString("strategyList")); + + private JButton strategyLoader = new JButton(Resources.getString("strategyActu")); + + private Vector<String> strategyList; + private JScrollPane jScrollPane; + private JList strategyBox; + private JTextArea strategyDescription = new JTextArea(Resources.getString("strategyDesc")); + private JLabel strategyMessage; + private boolean strategyMessInstall = false; + + private JPanel mainPanel = new JPanel(); + + private Object nextPageIdentifier = WizardPage.SUMMARY_PAGE_IDENTIFIER; + + private DictAccountRegistrationWizard wizard; + + private String initstrategy = ""; + + private ArrayList<String> strategiesAssoc = new ArrayList<String>(); + + private StrategyThread populateThread = null; + + private boolean firstAccount = false; + + /** + * Initial AccountID (null if new account) + * Used to check if there are modifications to the account + */ + private AccountID initAccountID = null; + + /** + * Creates an instance of <tt>FirstWizardPage</tt>. + * + * @param wizard the parent wizard + */ + public FirstWizardPage(DictAccountRegistrationWizard wizard) + { + super(new BorderLayout()); + + this.wizard = wizard; + + this.setPreferredSize(new Dimension(300, 150)); + + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + + this.populateThread = new StrategyThread(this); + + this.firstAccount = !this.hasAccount(); + + if (this.firstAccount) + { + this.initFirstAccount(); + } + else + { + this.init(); + } + + this.populateThread = new StrategyThread(this); + this.populateThread.start(); + + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + } + + /** + * Initializes all panels, buttons, etc. + */ + private void init() + { + // Host and port Field + this.hostField = new JTextField(); + this.portField = new JTextField("2628"); + + this.hostField.getDocument().addDocumentListener(this); + this.portField.getDocument().addDocumentListener(this); + + // Server informations + this.existingAccountLabel.setForeground(Color.RED); + + this.hostExampleLabel.setForeground(Color.GRAY); + this.hostExampleLabel.setFont(hostExampleLabel.getFont().deriveFont(8)); + this.emptyPanel.setMaximumSize(new Dimension(40, 35)); + this.hostExampleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, + 0)); + + labelsPanel.add(hostLabel); + labelsPanel.add(emptyPanel); + labelsPanel.add(portLabel); + + valuesPanel.add(hostField); + valuesPanel.add(hostExampleLabel); + valuesPanel.add(portField); + + hostPortPanel.add(labelsPanel, BorderLayout.WEST); + hostPortPanel.add(valuesPanel, BorderLayout.CENTER); + + hostPortPanel.setBorder(BorderFactory.createTitledBorder("Server informations")); + + this.labelsPanel.setLayout(new BoxLayout(labelsPanel, BoxLayout.Y_AXIS)); + this.valuesPanel.setLayout(new BoxLayout(valuesPanel, BoxLayout.Y_AXIS)); + + mainPanel.add(hostPortPanel); + + this.portField.addKeyListener(new KeyListener() { + public void keyTyped(KeyEvent evt) + { + // If evt isn't a digit, we don't add it + if (!Character.isDigit(evt.getKeyChar())) + { + evt.consume(); + } + } + + // Not used + public void keyPressed(KeyEvent evt) {;} + public void keyReleased(KeyEvent evt) {;} + }); + + // Strategies + this.strategyList = new Vector<String>(); + this.strategyBox = new JList(this.strategyList); + + for (int i=0; i<20; i++) + { + this.strategyList.add("Elem "+i); + } + + this.strategyBox.setVisibleRowCount(6); + + this.strategyBox.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + this.jScrollPane = new JScrollPane(); + this.jScrollPane.getViewport().add(this.strategyBox); + this.strategyPanel.add(this.jScrollPane); + + // Strategy title + button + this.strategyTitleBloc.add(this.strategyTitle, BorderLayout.WEST); + this.strategyTitleBloc.add(this.strategyLoader, BorderLayout.EAST); + + // Button action listener + this.strategyLoader.setActionCommand("populateList"); + this.strategyLoader.addActionListener(this); + + // South Panel + JPanel sSouthPanel = new JPanel(new BorderLayout()); + + // Description + this.strategyDescription.setLineWrap(true); + this.strategyDescription.setLineWrap(true); + this.strategyDescription.setRows(4); + this.strategyDescription.setWrapStyleWord(true); + this.strategyDescription.setAutoscrolls(false); + sSouthPanel.add(this.strategyDescription); + + // Message + this.strategyMessage = new JLabel(" "); + sSouthPanel.add(this.strategyMessage, BorderLayout.SOUTH); + + this.strategyPanel.add(sSouthPanel, BorderLayout.SOUTH); + + this.strategyPanel.add(this.strategyTitleBloc, BorderLayout.NORTH); + this.strategyPanel.setBorder(BorderFactory.createTitledBorder("Strategy selection")); + mainPanel.add(this.strategyPanel); + + this.add(mainPanel, BorderLayout.NORTH); + } + + private void initFirstAccount() + { + // Data init + this.hostField = new JTextField("dict.org"); + this.portField = new JTextField("2628"); + + // Init strategy box + this.strategyList = new Vector<String>(); + this.strategyBox = new JList(this.strategyList); + this.strategyMessage = new JLabel(" "); + + + JPanel infoTitlePanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + JTextArea firstDescription = new JTextArea(Resources.getString("firstAccount")); + JLabel title = new JLabel(Resources.getString("dictAccountInfoTitle")); + + // Title + title.setFont(title.getFont().deriveFont(Font.BOLD, 14.0f)); + infoTitlePanel.add(title); + this.add(infoTitlePanel, BorderLayout.NORTH); + this.add(this.strategyMessage, BorderLayout.SOUTH); + + // Description + firstDescription.setLineWrap(true); + firstDescription.setLineWrap(true); + firstDescription.setRows(6); + firstDescription.setWrapStyleWord(true); + firstDescription.setAutoscrolls(false); + this.add(firstDescription); + } + + /** + * Implements the <code>WizardPage.getIdentifier</code> to return this + * page identifier. + * + * @return Returns the identifier of the current (the first) page of the wizard. + */ + public Object getIdentifier() + { + return FIRST_PAGE_IDENTIFIER; + } + + /** + * Implements the <code>WizardPage.getNextPageIdentifier</code> to return + * the next page identifier - the summary page. + * + * @return Returns the identifier of the next page of the wizard. + */ + public Object getNextPageIdentifier() + { + return nextPageIdentifier; + } + + /** + * Implements the <code>WizardPage.getBackPageIdentifier</code> to return + * the next back identifier - the default page. + * + * @return Returns the identifier of the previous page of the wizard. + */ + public Object getBackPageIdentifier() + { + return WizardPage.DEFAULT_PAGE_IDENTIFIER; + } + + /** + * Implements the <code>WizardPage.getWizardForm</code> to return this + * panel. + * @return Returns this form of the wizard. + */ + public Object getWizardForm() + { + return this; + } + + /** + * Before this page is displayed enables or disables the "Next" wizard + * button according to whether the UIN field is empty. + */ + public void pageShowing() + { + this.setNextButtonEnabled(); + } + + /** + * Saves the user input when the "Next" wizard buttons is clicked. + */ + public void pageNext() + { + //* + String host = hostField.getText(); + int port = Integer.parseInt(portField.getText()); + int stPos; + boolean isModified = false; + + if (this.initAccountID instanceof AccountID) + { // We check if there is modifications to the server + String accHost = (String) this.initAccountID.getAccountProperties() + .get(ProtocolProviderFactory.SERVER_ADDRESS); + int accPort = Integer.parseInt((String) this.initAccountID.getAccountProperties() + .get(ProtocolProviderFactory.SERVER_PORT)); + + if (accHost != host || accPort != port) + { + isModified = true; + } + } + + // We check if a strategy has been selected + if (this.strategyList.size() == 0) + { // No Strategy, we get them + this.populateStrategies(); + + while (this.populateThread.isRunning()) {;} + } + + if (this.strategyList.size() == 0) + { + // No strategy, maybe not connected + // Information message is already on the wizard + nextPageIdentifier = FIRST_PAGE_IDENTIFIER; + this.revalidate(); + } + else if ((!wizard.isModification() && isExistingAccount(host, port)) + || (isModified && isExistingAccount(host, port))) + { + nextPageIdentifier = FIRST_PAGE_IDENTIFIER; + hostPortPanel.add(existingAccountLabel, BorderLayout.NORTH); + this.revalidate(); + } + else + { + nextPageIdentifier = SUMMARY_PAGE_IDENTIFIER; + hostPortPanel.remove(existingAccountLabel); + + DictAccountRegistration registration = wizard.getRegistration(); + + registration.setHost(host); + registration.setPort(port); + + stPos = this.strategyBox.getSelectedIndex(); + registration.setStrategyCode(this.strategiesAssoc.get(stPos)); + registration.setStrategy(this.strategyBox.getSelectedValue().toString()); + } + //*/ + } + + /** + * Enables or disables the "Next" wizard button according to whether the UIN + * field is empty. + */ + private void setNextButtonEnabled() + { + boolean hostOK = DictAdapter.isUrl(hostField.getText()); + boolean portOK = !this.portField.getText().isEmpty() + && Integer.parseInt(this.portField.getText()) > 10; + + if (this.firstAccount) + { + wizard.getWizardContainer().setNextFinishButtonEnabled(true); + } + else if (hostOK && portOK) + { + this.strategyLoader.setEnabled(true); + wizard.getWizardContainer().setNextFinishButtonEnabled(true); + } + else + { + // Disable the finish button + wizard.getWizardContainer().setNextFinishButtonEnabled(false); + + // Clear the list and disable the button + this.strategyList.clear(); + this.strategyLoader.setEnabled(false); + } + } + + /** + * Handles the <tt>DocumentEvent</tt> triggered when user types in the UIN + * field. Enables or disables the "Next" wizard button according to whether + * the UIN field is empty. + * + * @param e the <tt>DocumentEvent</tt> triggered when user types in the UIN + * field. + */ + public void insertUpdate(DocumentEvent e) + { + this.setNextButtonEnabled(); + } + + /** + * Handles the <tt>DocumentEvent</tt> triggered when user deletes letters + * from the UIN field. Enables or disables the "Next" wizard button + * according to whether the UIN field is empty. + * + * @param e The <tt>DocumentEvent</tt> triggered when user deletes letters + * from the UIN field. + */ + public void removeUpdate(DocumentEvent e) + { + this.setNextButtonEnabled(); + } + + /** + * Handles the <tt>DocumentEvent</tt> triggered when user changes an + * attribute or set of attributes from the UIN field. + * Currently this notification has no effect and is just here to implement + * the DocumentListener interface. + * + * @param e The <tt>DocumentEvent</tt> triggered when an attribute or set of + * attributes changed from the UIN field. + */ + public void changedUpdate(DocumentEvent e) + { + } + + /** + * Invoked when this WizardPage will be hidden eighter because the user has + * clicked "Back" or "Next". + * This function has no effect. + */ + public void pageHiding() + { + } + + /** + * Invoked when this WizardPage will be shown eighter because the user has + * clicked "Back" on the next wizard page or "Next" on the previous one. + * This function has no effect. + */ + public void pageShown() + { + } + + /** + * Invoked when user clicks on the "Back" wizard button. + * This function has no effect. + */ + public void pageBack() + { + } + + /** + * Fills the Host, Port and Strategy fields in this panel with the data comming + * from the given protocolProvider. + * + * @param protocolProvider The <tt>ProtocolProviderService</tt> to load + * the data from. + */ + public void loadAccount(ProtocolProviderService protocolProvider) + { + AccountID accountID = protocolProvider.getAccountID(); + String host = (String) accountID.getAccountProperties() + .get(ProtocolProviderFactory.SERVER_ADDRESS); + String port = (String) accountID.getAccountProperties() + .get(ProtocolProviderFactory.SERVER_PORT); + String strategy = (String) accountID.getAccountProperties() + .get(ProtocolProviderFactory.STRATEGY); + + this.initAccountID = accountID; + + // Host field + this.hostField.setText(host); + + // Port Field + this.portField.setText(port); + + // Load strategies + this.initstrategy = strategy; + this.populateStrategies(); + } + + /** + * Handles the action of the button. + * + * @param e The event generated when the button is pressed. + */ + public void actionPerformed(ActionEvent e) + { + // Button action -> populate the list + if (e.getActionCommand().equals("populateList")) + { + this.populateStrategies(); + } + } + + /** + * Checks if an account is stored for this protocol + * @return TRUE, if an account is stored - FALSE otherwise + */ + private boolean hasAccount() + { + ProtocolProviderFactory factory = + DictAccRegWizzActivator.getDictProtocolProviderFactory(); + + ArrayList registeredAccounts = factory.getRegisteredAccounts(); + return !registeredAccounts.isEmpty(); + } + + /** + * Checks if an acount with the given account already exists. + * + * @param host the host of the account to check + * @param port the port of the account to check + * @return TRUE, if an account with the given name already exists, FALSE - + * otherwise + */ + private boolean isExistingAccount(String host, int port) + { + //* + ProtocolProviderFactory factory = + DictAccRegWizzActivator.getDictProtocolProviderFactory(); + + ArrayList registeredAccounts = factory.getRegisteredAccounts(); + + String accHost; + int accPort; + + for (int i = 0; i < registeredAccounts.size(); i++) + { + AccountID accountID = (AccountID) registeredAccounts.get(i); + accHost = (String) accountID.getAccountProperties() + .get(ProtocolProviderFactory.SERVER_ADDRESS); + + if (host.equalsIgnoreCase(accHost)) + { + // We check the port, only if there is an account with the same host + accPort = Integer.parseInt((String) accountID.getAccountProperties() + .get(ProtocolProviderFactory.SERVER_PORT)); + + if (port == accPort) + { + return true; + } + } + } + //*/ + return false; + } + + /** + * Start the thread which will populate the Strategy List + */ + public void populateStrategies() + { + // Clear ArrayLists + this.strategiesAssoc.clear(); + this.strategyList.clear(); + + + //this.populateThread = new StrategyThread(this); + this.populateThread.setHost(this.hostField.getText()) + .setPort(Integer.parseInt(this.portField.getText())) + .sendProcessRequest(); + } + + /** + * Called by the thread, display a message + * @param message a message + */ + public void threadMessage(String message) + { + this.strategyMessage.setText(message); + } + + /** + * Called by the thread, remove the special message section + */ + public void threadRemoveMessage() + { + this.strategyMessage.setText(" "); + } + + /** + * Called by the thread, add a strategy in the list + * @param code The strategy code + * @param description The strategy description + */ + public void threadAddStrategy(String code, String description) + { + this.strategiesAssoc.add(code); + this.strategyList.add(description); + this.strategyBox.setListData(this.strategyList); + } + + /** + * Automatic selection of a strategy + */ + public void autoSelectStrategy() + { + int index = -1; + + if (this.initstrategy.length() > 0) + { // saved strategy + index = this.strategiesAssoc.indexOf(this.initstrategy); + this.initstrategy = ""; + } + if (index < 0) + { + // First case : levenstein distance + index = this.strategiesAssoc.indexOf("lev"); + } + if (index < 0) + { + // Second case : soundex + index = this.strategiesAssoc.indexOf("soundex"); + } + if (index < 0) + { + // Last case : prefix + index = this.strategiesAssoc.indexOf("prefix"); + } + + // If the index is still < 0, we select the first index + if (index < 0) + { + index = 0; + } + if (index < this.strategyBox.getVisibleRowCount()) + { + // If the index is visible row, we don't need to scroll + this.strategyBox.setSelectedIndex(index); + } + else + { + // Otherwise, we scroll to the selected value + this.strategyBox.setSelectedValue(this.strategyList.get(index), true); + } + + } + + /** + * Enables or disable the Next Button and the Strategy Button + * @param e TRUE enables - FALSE disables + */ + public void setStrategyButtonEnable(boolean e) + { + // During all the process the buttons and the fieldsset are in the same state + + this.hostField.setEnabled(e); + this.portField.setEnabled(e); + + this.strategyLoader.setEnabled(e); + wizard.getWizardContainer().setNextFinishButtonEnabled(e); + } +} diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/Resources.java b/src/net/java/sip/communicator/plugin/dictaccregwizz/Resources.java new file mode 100644 index 0000000..c86ebd6 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/Resources.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.plugin.dictaccregwizz; + +import java.io.*; +import java.util.*; + +import net.java.sip.communicator.util.*; + +/** + * The <tt>Resources</tt> class manages the access to the internationalization + * properties files and the image resources used in this plugin. + * + * @author ROTH Damien + * @author LITZELMAN Cédric + */ +public class Resources +{ + private static Logger log = Logger.getLogger(Resources.class); + + /** + * The name of the resource, where internationalization strings for this + * plugin are stored. + */ + private static final String STRING_RESOURCE_NAME + = "resources.languages.plugin.dictaccregwizz.resources"; + + /** + * The name of the resource, where paths to images used in this bundle are + * stored. + */ + private static final String IMAGE_RESOURCE_NAME + = "net.java.sip.communicator.plugin.dictaccregwizz.resources"; + + /** + * The string resource bundle. + */ + private static final ResourceBundle STRING_RESOURCE_BUNDLE + = ResourceBundle.getBundle(STRING_RESOURCE_NAME); + + /** + * The image resource bundle. + */ + private static final ResourceBundle IMAGE_RESOURCE_BUNDLE + = ResourceBundle.getBundle(IMAGE_RESOURCE_NAME); + + public static ImageID DICT_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 STRING_RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + //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 = IMAGE_RESOURCE_BUNDLE.getString(imageID.getId()); + + try + { + Resources.class.getClassLoader() + .getResourceAsStream(path).read(image); + } + catch (IOException e) + { + log.error("Failed to load image:" + path, e); + } + + return image; + } + + /** + * Represents the Image Identifier. + */ + public static class ImageID + { + private String id; + + private ImageID(String id) + { + this.id = id; + } + + public String getId() + { + return id; + } + } +} diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/StrategyThread.java b/src/net/java/sip/communicator/plugin/dictaccregwizz/StrategyThread.java new file mode 100644 index 0000000..24754e9 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/StrategyThread.java @@ -0,0 +1,207 @@ +/* + * 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.dictaccregwizz; + +import java.util.*; + +import net.java.sip.communicator.util.*; + +/** + * The <tt>StrategyThread</tt> is the thread called by the wizzard to populate + * the strategies list. + * + * @author ROTH Damien + * @author LITZELMANN Cédric + */ +public class StrategyThread + extends Thread +{ + private static Logger logger = Logger.getLogger(StrategyThread.class); + + /** + * The hostname of the DICT server. + */ + private String host; + + /** + * The port used by the DICT server. + */ + private int port; + + /** + * True if the thread is running. + */ + private boolean isRunning = false; + + /** + * True if we need to search the strategies handled by the server in order + * to populate the list. + */ + private boolean needProcess = false; + + /** + * The first page wizard for the DICT protocole. + */ + private FirstWizardPage wizard; + + /** + * The java abstraction of the DICT server. + */ + private DictAdapter adapter = null; + + /** + * Create a new StrategyThread + * @param wizard the wizard for callback methods + */ + public StrategyThread(FirstWizardPage wizard) + { + this.wizard = wizard; + } + + /** + * Thread method running until it's destroy + */ + public void run() + { + while (true) + { + if (this.needProcess()) + { + this.setRunning(true); + this.process(); + this.processDone(); + this.setRunning(false); + } + try + { + this.sleep(500); + } + catch (InterruptedException ie) + { + // Action de log + logger.info("DICT THREAD : " + ie); + } + } + } + + /** + * Search the strategies on the server and populate the list + */ + private void process() + { + ArrayList<String> strategies = null; + String temp[]; + + if (adapter == null) { + adapter = new DictAdapter(); + } + + this.wizard.setStrategyButtonEnable(false); + + // Initialize the connexion + this.wizard.threadMessage("Trying to connect to server"); + if (!adapter.connect(this.host, this.port)) + { + // Connexion attempt failed + this.wizard.threadMessage("Connexion attempt failed, this isn't a" + + "dict server or the server is offline"); + return; + } + + // Retrieving strategies + this.wizard.threadMessage("Retrieving strategies"); + strategies = adapter.getStrategies(); + if (strategies == null) + { + // No strategy found + this.wizard.threadMessage("No strategy found on the server"); + return; + } + + + // Insert the strategies in the list + for (int i=0; i<strategies.size(); i++) + { + temp = strategies.get(i).split(" ", 2); + + this.wizard.threadAddStrategy(temp[0], temp[1].replace("\"", "")); + } + + this.wizard.autoSelectStrategy(); + + // Closing connexion + this.wizard.threadMessage("Closing connexion"); + adapter.close(); + + this.wizard.threadRemoveMessage(); + this.wizard.setStrategyButtonEnable(true); + } + + /** + * Set the hostname of the dict server + * @param host The hostname of the server. + * @return The thread for populating strategie list. + */ + public StrategyThread setHost(String host) + { + this.host = host; + return this; + } + + /** + * Set the port of the dict server + * @param port The port of the DICT server. + * @return The thread for populating strategie list. + */ + public StrategyThread setPort(int port) + { + this.port = port; + return this; + } + + /** + * Checks if the thread is processing a query + * @return TRUE if the thread is processing a query - FALSE otherwise + */ + public synchronized boolean isRunning() + { + return this.isRunning || this.needProcess; + } + + /** + * Marks the thread as running or not + * @param r Activate or desactivate the strategie thread. + */ + public synchronized void setRunning(boolean r) + { + this.isRunning = r; + } + + /** + * Checks if we need to process a query + * @return Returns true if we need to search and populate the strategie list. False otherwise. + */ + private synchronized boolean needProcess() + { + return this.needProcess; + } + + /** + * Init the request + */ + public synchronized void sendProcessRequest() + { + this.needProcess = true; + } + + /** + * Defines that the thread has done the query + */ + private synchronized void processDone() + { + this.needProcess = false; + } +} diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/dictaccregwizz.manifest.mf b/src/net/java/sip/communicator/plugin/dictaccregwizz/dictaccregwizz.manifest.mf new file mode 100644 index 0000000..4dcab77 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/dictaccregwizz.manifest.mf @@ -0,0 +1,31 @@ +Bundle-Activator: net.java.sip.communicator.plugin.dictaccregwizz.DictAccRegWizzActivator +Bundle-Name: Dict account registration wizard +Bundle-Description: Dict account registration wizard. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.osgi.framework, + net.java.sip.communicator.util, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.configuration.event, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.protocol.icqconstants, + net.java.sip.communicator.service.protocol.event, + net.java.sip.communicator.service.contactlist, + net.java.sip.communicator.service.contactlist.event, + net.java.sip.communicator.service.gui, + net.java.sip.communicator.service.gui.event, + 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 diff --git a/src/net/java/sip/communicator/plugin/dictaccregwizz/resources.properties b/src/net/java/sip/communicator/plugin/dictaccregwizz/resources.properties new file mode 100644 index 0000000..b1146b3 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/dictaccregwizz/resources.properties @@ -0,0 +1,2 @@ +protocolIcon=resources/images/protocol/dict/dict-16x16.png +pageImage=resources/images/protocol/dict/dict-64x64.png |