diff options
author | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2017-03-11 22:15:03 +0100 |
---|---|---|
committer | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2017-03-11 22:15:03 +0100 |
commit | 85901329b0794b136b96bf745f4ab1572806fc89 (patch) | |
tree | f23da7e97cae727f39d825f0fef8348cffb238e4 /src/net/java/sip/communicator/impl/protocol | |
parent | 3db2e44f186c59429901b2c899e139ea60117a55 (diff) | |
parent | cf5da997da8820b4050f5b87ee9440a0ede36d1f (diff) | |
download | jitsi-85901329b0794b136b96bf745f4ab1572806fc89.zip jitsi-85901329b0794b136b96bf745f4ab1572806fc89.tar.gz jitsi-85901329b0794b136b96bf745f4ab1572806fc89.tar.bz2 |
Signed-off-by: Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de>
Diffstat (limited to 'src/net/java/sip/communicator/impl/protocol')
165 files changed, 8453 insertions, 34524 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 deleted file mode 100644 index 924c0f7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/ContactDictImpl.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.dict; - -import net.java.dict4j.*; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * An implementation of a Dict contact - * - * @author ROTH Damien - * @author LITZELMANN Cedric - */ -public class ContactDictImpl - extends AbstractContact -{ - private Logger logger = Logger.getLogger(ContactDictImpl.class); - - /** - * Icon - */ - private static byte[] icon = DictActivator.getResources() - .getImageInBytes("service.protocol.dict.DICT_64x64"); - - /** - * 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; - - /** - * The string in a "humain readable and understandable representation" of - * the dictionnaire. In brief this is a short description of the dictionary. - */ - private String dictName = null; - - /** - * Creates an instance of a meta contact with the specified string used - * as a name and identifier. - * - * @param databaseCode 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() - { - if (dictName == null) - { - if (this.contactID.equals("*")) - { - this.dictName = DictActivator.getResources() - .getI18NString("plugin.dictaccregwizz.ANY_DICTIONARY"); - } - else if (this.contactID.equals("!")) - { - this.dictName = DictActivator.getResources() - .getI18NString("plugin.dictaccregwizz.FIRST_MATCH"); - } - else - { - try - { - this.dictName = this.parentProvider.getConnection() - .getDictionaryName(this.contactID); - } - catch (DictException dx) - { - logger.error("Error while getting dictionary long name", dx); - } - - if (this.dictName == null) - this.dictName = this.contactID; - } - } - - return dictName; - } - - /** - * 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 icon; - } - - /** - * 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; - } - - /** - * 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. - */ - @Override - 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; - } - - /** - * 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 deleted file mode 100644 index e52ce96..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/ContactGroupDictImpl.java +++ /dev/null @@ -1,588 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -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 Cedric - */ -public class ContactGroupDictImpl - implements ContactGroup -{ - - /** - * The name of this Dict contact group. - */ - private String groupName = null; - - /** - * The list of this group's members. - */ - private List<Contact> contacts = new ArrayList<Contact>(); - - /** - * The list of sub groups belonging to this group. - */ - private List<ContactGroup> subGroups = new ArrayList<ContactGroup>(); - - /** - * 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<Contact> 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 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<ContactGroup> 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<ContactGroup> 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<Contact> 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 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<ContactGroup> 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<ContactGroup> 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<Contact> 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<ContactGroup> 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. - */ - @Override - public String toString() - { - - StringBuffer buff = new StringBuffer(getGroupName()); - buff.append(".subGroups=" + countSubgroups() + ":\n"); - - Iterator<ContactGroup> 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<Contact> 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. - */ - @Override - 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<Contact> 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<ContactGroup> 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 deleted file mode 100644 index 538ae71..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/DictAccountID.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.dict; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * The Dict implementation of a sip-communicator account id. - * @author LITZELMANN Cedric - * @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<String, String> accountProperties) - { - super(userID, accountProperties, ProtocolNames.DICT, "dict.org"); - } - - /** - * Returns the dict server adress - * @return the dict server adress - */ - public String getHost() - { - return getAccountPropertyString(ProtocolProviderFactory.SERVER_ADDRESS); - } - - /** - * Returns the dict server port - * @return the dict server port - */ - public int getPort() - { - return Integer - .parseInt(getAccountPropertyString(ProtocolProviderFactory.SERVER_PORT)); - } - - /** - * Returns the selected strategy - * @return the selected strategy - */ - public String getStrategy() - { - return getAccountPropertyString(ProtocolProviderFactory.STRATEGY); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java b/src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java deleted file mode 100644 index b5976c7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -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 org.jitsi.service.resources.*; -import org.osgi.framework.*; - -/** - * Loads the Dict provider factory and registers its services in the OSGI - * bundle context. - * - * @author ROTH Damien - * @author LITZELMANN Cedric - */ -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; - - private static ResourceManagementService resourceService; - - /** - * 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<String,String> hashtable = new Hashtable<String,String>(); - hashtable.put(ProtocolProviderFactory.PROTOCOL, ProtocolNames.DICT); - - dictProviderFactory = new ProtocolProviderFactoryDictImpl(); - - //reg the dict provider factory. - dictPpFactoryServReg = context.registerService( - ProtocolProviderFactory.class.getName(), - dictProviderFactory, - hashtable); - - if (logger.isInfoEnabled()) - 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 - { - - dictProviderFactory.stop(); - dictPpFactoryServReg.unregister(); - - if (logger.isInfoEnabled()) - logger.info("DICT protocol implementation [STOPPED]."); - } - - /** - * Returns 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; - } - - - /** - * Returns the <tt>ResourceManagementService</tt>. - * - * @return the <tt>ResourceManagementService</tt>. - */ - public static ResourceManagementService getResources() - { - if (resourceService == null) - { - ServiceReference serviceReference = bundleContext - .getServiceReference(ResourceManagementService.class.getName()); - - if(serviceReference == null) - return null; - - resourceService = (ResourceManagementService) bundleContext - .getService(serviceReference); - } - - return resourceService; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.java b/src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.java deleted file mode 100644 index 87086f7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.dict; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * An implementation of <tt>PresenceStatus</tt> that enumerates all states that - * a Dict contact can fall into. - * - * @author ROTH Damien - * @author LITZELMANN Cedric - */ -public class DictStatusEnum - extends PresenceStatus -{ - - /** - * Indicates an Offline status or status with 0 connectivity. - */ - public static final DictStatusEnum OFFLINE - = new DictStatusEnum( - 0, "Offline", - DictActivator.getResources() - .getImageInBytes("service.protocol.dict.OFFLINE_STATUS_ICON")); - - /** - * The Online status. Indicate that the user is able and willing to - * communicate. - */ - public static final DictStatusEnum ONLINE - = new DictStatusEnum( - 65, "Online", - DictActivator.getResources() - .getImageInBytes("service.protocol.dict.DICT_16x16")); - - /** - * Initialize the list of supported status states. - */ - private static List<PresenceStatus> supportedStatusSet = new LinkedList<PresenceStatus>(); - 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<PresenceStatus> supportedStatusSet() - { - return supportedStatusSet.iterator(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/MessageDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/MessageDictImpl.java deleted file mode 100644 index 5b95dd4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/MessageDictImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -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 Cedric - * @author Lubomir Marinov - */ -public class MessageDictImpl - extends AbstractMessage -{ - - /** - * 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) - { - super(content, contentType, contentEncoding, 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 deleted file mode 100644 index f1f4973..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.dict; - -import java.util.*; - -import net.java.dict4j.*; -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 Cedric - */ -public class OperationSetBasicInstantMessagingDictImpl - extends AbstractOperationSetBasicInstantMessaging - implements RegistrationStateChangeListener -{ - /** - * The currently valid persistent presence operation set. - */ - private OperationSetPersistentPresenceDictImpl opSetPersPresence = null; - - /** - * The protocol provider that created us. - */ - private ProtocolProviderServiceDictImpl parentProvider = null; - - private DictAccountID accountID; - - /** - * 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; - this.accountID = (DictAccountID) provider.getAccountID(); - - parentProvider.addRegistrationStateChangeListener(this); - } - - @Override - public Message createMessage(String content) - { - return new MessageDictImpl(content, HTML_MIME_TYPE, - DEFAULT_MIME_ENCODING, null); - } - - @Override - public Message createMessage(String content, String contentType, - String encoding, String subject) - { - return new MessageDictImpl(content, contentType, encoding, subject); - } - - /** - * 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); - } - - // Remove all html tags from the message - message = createMessage(Html2Text.extractText(message.getContent())); - - // Display the queried word - fireMessageDelivered(message, to); - - this.submitDictQuery((ContactDictImpl) to, message); - } - - /** - * 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(HTML_MIME_TYPE)) - 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(); - DictConnection conn = this.parentProvider.getConnection(); - boolean doMatch = false; - - 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 - { - List<Definition> definitions = conn.define(database, word); - msg = this.createMessage(retrieveDefine(definitions, word)); - } - catch(DictException dx) - { - if (dx.getErrorCode() == DictReturnCode.NO_MATCH) - { // 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(manageException(dx, database)); - } - } - - if (doMatch) - { - // Trying the match command - try - { - List<MatchWord> matchWords = conn.match(database, word, - this.accountID.getStrategy()); - msg = this.createMessage(retrieveMatch(matchWords, word)); - } - catch(DictException dx) - { - msg = this.createMessage(manageException(dx, database)); - } - } - - // 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(List<Definition> data, String word) - { - StringBuffer res = new StringBuffer(); - Definition def; - - for (int i=0; i<data.size(); i++) - { - def = data.get(i); - - if(i != 0 && data.size() > 0) - { - res.append("<hr>"); - } - res.append(def.getDefinition().replaceAll("\n", "<br>")) - .append("<div align=\"right\"><font size=\"-2\">-- From ") - .append(def.getDictionary()) - .append("</font></div>"); - } - - String result = res.toString(); - result = formatResult(result, "\\\\", "<em>", "</em>"); - result = formatResult(result, "[\\[\\]]", "<cite>", "</cite>"); - result = formatResult(result, "[\\{\\}]", "<strong>", "</strong>"); - result = formatWordDefined(result, word); - - return result; - } - - /** - * Makes a stronger emphasis for the word defined. - * @param result The text containing the definition of the word. - * @param word The word defined to display with bold font. For this we - * had the strong HTML tag. - * @return Returns the result text with an strong emphasis of all - * the occurences of the word defined. - */ - private String formatWordDefined(String result, String word) - { - String tmpWord; - - tmpWord = word.toUpperCase(); - result = result.replaceAll("\\b" + tmpWord + "\\b", "<strong>" + tmpWord + "</strong>"); - tmpWord = word.toLowerCase(); - result = result.replaceAll("\\b" + tmpWord + "\\b", "<strong>" + tmpWord + "</strong>"); - if(tmpWord.length() > 1) - { - tmpWord = tmpWord.substring(0, 1).toUpperCase() + tmpWord.substring(1); - result = result.replaceAll("\\b" + tmpWord + "\\b", "<strong>" + tmpWord + "</strong>"); - } - - return result; - } - - /** - * Remplaces special characters into HTML tags to make some emphasis. - * @param result The text containing the definition of the word. - * @param regex The special character to replace with HTML tags. - * @param startTag The start HTML tag to use. - * @param endTag The end HTML tag to use. - * @return The result with all special characters replaced by HTML - * tags. - */ - private String formatResult(String result, String regex, String startTag, String endTag) - { - String[] tmp = result.split(regex); - String res = ""; - - for(int i = 0; i < (tmp.length - 1); i += 2) - { - res += tmp[i] + startTag + tmp[i+1] + endTag; - } - if((tmp.length % 2) != 0) - { - res += tmp[tmp.length - 1]; - } - return res; - } - - /** - * 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(List<MatchWord> data, String word) - { - StringBuffer result = new StringBuffer(); - boolean isStart = true; - - result.append(DictActivator.getResources() - .getI18NString("plugin.dictaccregwizz.MATCH_RESULT", new String[] {word})); - - for (int i=0; i<data.size(); i++) - { - if (isStart) - isStart = false; - else - result.append(", "); - - result.append(data.get(i).getWord()); - } - - return result.toString(); - } - - /** - * Manages the return exception of a dict query. - * - * @param dix The exception returned by the adapter - * @param database The dictionary used - * @return Exception message - */ - private String manageException(DictException dix, String database) - { - int errorCode = dix.getErrorCode(); - - // We change the text only for exception 550 (invalid dictionary) and 551 (invalid strategy) - if (errorCode == DictReturnCode.INVALID_DATABASE) - { - return DictActivator.getResources() - .getI18NString("plugin.dictaccregwizz.INVALID_DATABASE", new String[] {database}); - } - else if (errorCode == DictReturnCode.INVALID_STRATEGY) - { - return DictActivator.getResources() - .getI18NString("plugin.dictaccregwizz.INVALID_STRATEGY"); - } - else if (errorCode == DictReturnCode.NO_MATCH) - { - return DictActivator.getResources() - .getI18NString("plugin.dictaccregwizz.NO_MATCH"); - } - - return dix.getMessage(); - } - - /** - * Sends the <tt>message</tt> to the destination indicated by the - * <tt>to</tt>. Resources are not supported by this operation set - * implementation. - * - * @param to the <tt>Contact</tt> to send <tt>message</tt> to - * @param toResource the resource to which the message should be send - * @param message the <tt>Message</tt> to send. - * @throws java.lang.IllegalStateException if the underlying ICQ stack is - * not registered and initialized. - * @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an - * instance belonging to the underlying implementation. - */ - @Override - public void sendInstantMessage( Contact to, - ContactResource toResource, - Message message) - throws IllegalStateException, - IllegalArgumentException - { - sendInstantMessage(to, message); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java deleted file mode 100644 index 8184fe9..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java +++ /dev/null @@ -1,988 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -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 Cedric - */ -public class OperationSetPersistentPresenceDictImpl - extends AbstractOperationSetPersistentPresence<ProtocolProviderServiceDictImpl> -{ - private static final Logger logger = - Logger.getLogger(OperationSetPersistentPresenceDictImpl.class); - - /** - * The root of the dict contact list. - */ - private ContactGroupDictImpl contactListRoot = 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) - { - super(provider); - - contactListRoot = new ContactGroupDictImpl("RootGroup", provider); - - //add our unregistration listener - parentProvider.addRegistrationStateChangeListener( - new UnregistrationListener()); - } - - /** - * 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<PresenceStatus> 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<Contact> contacts = findContactsPointingToUs(); - - Iterator<Contact> 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<Contact> 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<ContactGroup> subgroups = parent.subgroups(); - - while(subgroups.hasNext()) - { - ContactGroup subgroup = subgroups.next(); - changePresenceStatusForAllContacts(subgroup, newStatus); - } - } - - /** - * 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); - } - - /** - * 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( - 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 - * destination <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(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<Contact> findContactsPointingToUs() - { - List<Contact> contacts = new LinkedList<Contact>(); - 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<ContactGroup> groupsIter = getServerStoredContactListRoot() - .subgroups(); - while (groupsIter.hasNext()) - { - ContactGroupDictImpl group - = (ContactGroupDictImpl) groupsIter.next(); - - Iterator<Contact> 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) - { - theVolatileGroup = new ContactGroupDictImpl( - DictActivator.getResources().getI18NString( - "service.gui.NOT_IN_CONTACT_LIST_GROUP_NAME") - , 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 deleted file mode 100644 index 4f09f22..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolIconDictImpl.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.dict; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * Represents 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 Cedric - */ -public class ProtocolIconDictImpl - implements ProtocolIcon -{ - /** - * 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, - DictActivator.getResources() - .getImageInBytes("service.protocol.dict.DICT_16x16")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_32x32, - DictActivator.getResources() - .getImageInBytes("service.protocol.dict.DICT_32x32")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_48x48, - DictActivator.getResources() - .getImageInBytes("service.protocol.dict.DICT_48x48")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_64x64, - DictActivator.getResources() - .getImageInBytes("service.protocol.dict.DICT_64x64")); - } - - /** - * A hash table containing the path to the protocol icon in different sizes. - */ - private static Hashtable<String, String> iconPathsTable - = new Hashtable<String, String>(); - static - { - iconPathsTable.put(ProtocolIcon.ICON_SIZE_16x16, - DictActivator.getResources() - .getImagePath("service.protocol.dict.DICT_16x16")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_32x32, - DictActivator.getResources() - .getImagePath("service.protocol.dict.DICT_32x32")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_48x48, - DictActivator.getResources() - .getImagePath("service.protocol.dict.DICT_48x48")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_64x64, - DictActivator.getResources() - .getImagePath("service.protocol.dict.DICT_64x64")); - } - - /** - * 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<String> 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 a path to the icon with the given size. - * @param iconSize the size of the icon we're looking for - * @return the path to the icon with the given size - */ - public String getIconPath(String iconSize) - { - return iconPathsTable.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); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderFactoryDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderFactoryDictImpl.java deleted file mode 100644 index d7eaf30..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderFactoryDictImpl.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.dict; - -import java.util.*; - -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 Cedric - */ -public class ProtocolProviderFactoryDictImpl - extends ProtocolProviderFactory -{ - private static final Logger logger - = Logger.getLogger(ProtocolProviderFactoryDictImpl.class); - - /** - * Creates an instance of the ProtocolProviderFactoryDictImpl. - */ - public ProtocolProviderFactoryDictImpl() - { - super(DictActivator.getBundleContext(), ProtocolNames.DICT); - } - - /** - * Initializaed and creates an account corresponding to the specified - * accountProperties and registers the resulting ProtocolProvider in the - * <tt>context</tt> BundleContext parameter. - * - * @param userIDStr The 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. - */ - @Override - public AccountID installAccount( String userIDStr, - Map<String, String> 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(accountID, false); - - accountID = loadAccount(accountProperties); - - // Creates the dict contact group. - this.createGroup(); - // Creates the default conatct for this dict server. - this.createDefaultContact(accountID); - - return accountID; - } - - @Override - protected AccountID createAccountID(String userID, Map<String, String> accountProperties) - { - return new DictAccountID(userID, accountProperties); - } - - @Override - protected ProtocolProviderService createService(String userID, - AccountID accountID) - { - ProtocolProviderServiceDictImpl service = - new ProtocolProviderServiceDictImpl(); - - service.initialize(userID, accountID); - return service; - } - - /** - * Creates a group for the dict contacts - */ - private void createGroup() - { - // Get MetaContactListService - BundleContext bundleContext = getBundleContext(); - ServiceReference<MetaContactListService> mfcServiceRef - = bundleContext.getServiceReference(MetaContactListService.class); - - MetaContactListService mcl = bundleContext.getService(mfcServiceRef); - - try - { - String groupName = DictActivator.getResources() - .getI18NString("service.protocol.DICTIONARIES"); - - mcl.createMetaContactGroup(mcl.getRoot(), groupName); - } - catch (MetaContactListException ex) - { - int errorCode = ex.getErrorCode(); - if (errorCode != MetaContactListException.CODE_GROUP_ALREADY_EXISTS_ERROR) - { - logger.error(ex); - } - } - } - - /** - * Creates a default contact for the new DICT server. - * @param accountID The accountID of the dict protocol provider for which we - * want to add a default contact. - */ - private void createDefaultContact(AccountID accountID) - { - // Gets the MetaContactListService. - BundleContext bundleContext = getBundleContext(); - ServiceReference<MetaContactListService> mfcServiceRef - = bundleContext.getServiceReference(MetaContactListService.class); - MetaContactListService mcl = bundleContext.getService(mfcServiceRef); - - // Gets the ProtocolProviderService. - ServiceReference<ProtocolProviderService> serRef - = getProviderForAccount(accountID); - ProtocolProviderService protocolProvider - = DictActivator.getBundleContext().getService(serRef); - - // Gets group name - String groupName = DictActivator.getResources() - .getI18NString("service.protocol.DICTIONARIES"); - - // Gets contact name - String contactName = DictActivator.getResources() - .getI18NString("plugin.dictaccregwizz.ANY_DICTIONARY_FORM", - new String[] {accountID.getUserID()}); - - // Gets the MetaContactGroup for the "dictionaries" group. - MetaContactGroup group = mcl.getRoot().getMetaContactSubgroup(groupName); - - // Sets the default contact identifier to "*" corresponding to "all the - // dictionaries" available on the server (cf. RFC-2229). - String dict_uin = "*"; - // Create the default contact. - mcl.createMetaContact(protocolProvider, group, dict_uin); - // Rename the default contact. - mcl.renameMetaContact( - group.getMetaContact(protocolProvider, dict_uin), - contactName); - } - - @Override - public void modifyAccount( - ProtocolProviderService protocolProvider, - Map<String, String> accountProperties) - throws NullPointerException - { - // TODO Auto-generated method stub - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderServiceDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderServiceDictImpl.java deleted file mode 100644 index aa9ef50..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderServiceDictImpl.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.dict; - -import net.java.dict4j.*; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; - -import org.jitsi.service.version.*; -import org.osgi.framework.*; - -/** - * A Dict implementation of the ProtocolProviderService. - * - * @author ROTH Damien - * @author LITZELMANN Cedric - */ -public class ProtocolProviderServiceDictImpl - extends AbstractProtocolProviderService -{ - 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 DictAccountID accountID = null; - - /** - * We use this to lock access to initialization. - */ - private Object initializationLock = new Object(); - - /** - * 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 <tt>DictConnection</tt> opened by this provider - */ - private DictConnection dictConnection; - - /** - * The default constructor for the Dict protocol provider. - */ - public ProtocolProviderServiceDictImpl() - { - if (logger.isTraceEnabled()) - 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 = (DictAccountID) accountID; - - this.dictConnection = new DictConnection(this.accountID.getHost(), - this.accountID.getPort()); - this.dictConnection.setClientName(getSCVersion()); - - //initialize the presence operationset - OperationSetPersistentPresenceDictImpl persistentPresence = - new OperationSetPersistentPresenceDictImpl(this); - - addSupportedOperationSet( - OperationSetPersistentPresence.class, - persistentPresence); - //register it once again for those that simply need presence and - //won't be smart enough to check for a persistent presence - //alternative - addSupportedOperationSet( - OperationSetPresence.class, - persistentPresence); - - //initialize the IM operation set - addSupportedOperationSet( - OperationSetBasicInstantMessaging.class, - new OperationSetBasicInstantMessagingDictImpl( - this, - persistentPresence)); - - //initialize the typing notifications operation set - /*OperationSetTypingNotifications typingNotifications = - new OperationSetTypingNotificationsDictImpl( - this, persistentPresence); - - supportedOperationSets.put( - OperationSetTypingNotifications.class.getName(), - typingNotifications); - */ - isInitialized = true; - } - } - - /** - * Returns the <tt>DictConnection</tt> opened by this provider - * @return the <tt>DictConnection</tt> opened by this provider - */ - public DictConnection getConnection() - { - return this.dictConnection; - } - - /** - * 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 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 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; - } - - /** - * 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 - { - // Try to connect to the server - boolean connected = connect(); - - if (connected) - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.REGISTERED, - RegistrationStateChangeEvent.REASON_USER_REQUEST, - null); - currentRegistrationState = RegistrationState.REGISTERED; - } - else - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.CONNECTION_FAILED, - RegistrationStateChangeEvent.REASON_SERVER_NOT_FOUND, - null); - currentRegistrationState = RegistrationState.UNREGISTERED; - } - } - - /** - * Checks if the connection to the dict server is open - * @return TRUE if the connection is open - FALSE otherwise - */ - private boolean connect() - { - if (this.dictConnection.isConnected()) - { - return true; - } - - try - { - return this.dictConnection.isAvailable(); - } - catch (DictException dx) - { - if (logger.isInfoEnabled()) - logger.info(dx); - } - - return false; - } - - /** - * 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; - } - if (logger.isTraceEnabled()) - logger.trace("Killing the Dict Protocol Provider for account " - + this.accountID.getUserID()); - - closeConnection(); - - 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 - { - closeConnection(); - - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.UNREGISTERED, - RegistrationStateChangeEvent.REASON_USER_REQUEST, - null); - } - - /** - * DICT has no support for secure transport. - */ - public boolean isSignalingTransportSecure() - { - return false; - } - - /** - * Returns the "transport" protocol of this instance used to carry the - * control channel for the current protocol service. - * - * @return The "transport" protocol of this instance: TCP. - */ - public TransportProtocol getTransportProtocol() - { - return TransportProtocol.TCP; - } - - /** - * Close the connection to the server - */ - private void closeConnection() - { - try - { - this.dictConnection.close(); - } - catch (DictException dx) - { - if (logger.isInfoEnabled()) - logger.info(dx); - } - } - - /** - * Returns the current version of SIP-Communicator - * @return the current version of SIP-Communicator - */ - private String getSCVersion() - { - BundleContext bc = DictActivator.getBundleContext(); - ServiceReference vsr = bc.getServiceReference(VersionService.class.getName()); - - VersionService vs = (VersionService) bc.getService(vsr); - return vs.getCurrentVersion().toString(); - - } -} 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 deleted file mode 100644 index d764657..0000000 --- a/src/net/java/sip/communicator/impl/protocol/dict/dict.provider.manifest.mf +++ /dev/null @@ -1,15 +0,0 @@ -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: jitsi.org -Bundle-Version: 1.0.0 -Bundle-SymbolicName: net.java.sip.communicator.protocol.dict -Import-Package: org.osgi.framework, - org.jitsi.service.version, - net.java.sip.communicator.service.contactlist, - org.jitsi.service.configuration, - org.jitsi.service.resources, net.java.sip.communicator.service.resources, - net.java.sip.communicator.util, - net.java.sip.communicator.service.protocol, - net.java.sip.communicator.service.protocol.event, -Export-Package: net.java.dict4j diff --git a/src/net/java/sip/communicator/impl/protocol/gibberish/ContactGroupGibberishImpl.java b/src/net/java/sip/communicator/impl/protocol/gibberish/ContactGroupGibberishImpl.java index 2a95493..8528267 100644 --- a/src/net/java/sip/communicator/impl/protocol/gibberish/ContactGroupGibberishImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/gibberish/ContactGroupGibberishImpl.java @@ -577,4 +577,29 @@ public class ContactGroupGibberishImpl return true; } + + @Override + public int hashCode() + { + List<Object> objects = new ArrayList<Object>(); + objects.add(getGroupName()); + objects.add(getUID()); + objects.add(countContacts()); + objects.add(countSubgroups()); + + //traverse child contacts + for (Contact c : contacts) + { + objects.add(c.getAddress()); + } + + + //traverse subgroups + for (ContactGroup g : subGroups) + { + objects.add(g.getGroupName()); + } + + return Objects.hash(objects.toArray()); + } } diff --git a/src/net/java/sip/communicator/impl/protocol/icq/icq.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/icq/icq.provider.manifest.mf index da47277..959ae6d 100644 --- a/src/net/java/sip/communicator/impl/protocol/icq/icq.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/icq/icq.provider.manifest.mf @@ -13,5 +13,4 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.dns, net.java.sip.communicator.service.protocol, net.java.sip.communicator.service.protocol.icqconstants, - net.java.sip.communicator.service.protocol.aimconstants, net.java.sip.communicator.service.protocol.event diff --git a/src/net/java/sip/communicator/impl/protocol/irc/OperationSetMultiUserChatIrcImpl.java b/src/net/java/sip/communicator/impl/protocol/irc/OperationSetMultiUserChatIrcImpl.java index 6b09978..7a1c2b3 100644 --- a/src/net/java/sip/communicator/impl/protocol/irc/OperationSetMultiUserChatIrcImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/irc/OperationSetMultiUserChatIrcImpl.java @@ -275,7 +275,7 @@ public class OperationSetMultiUserChatIrcImpl */ protected ChatRoomIrcImpl getChatRoom(final String chatRoomName) { - return (ChatRoomIrcImpl) this.chatRoomCache.get(chatRoomName); + return this.chatRoomCache.get(chatRoomName); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/AnonymousLoginStrategy.java b/src/net/java/sip/communicator/impl/protocol/jabber/AnonymousLoginStrategy.java index c955100..b62d01a 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/AnonymousLoginStrategy.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/AnonymousLoginStrategy.java @@ -70,7 +70,7 @@ public class AnonymousLoginStrategy } @Override - public boolean login(XMPPConnection connection, String userName, + public boolean login(Connection connection, String userName, String resource) throws XMPPException { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java index e23481a..3987ea8 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java @@ -311,7 +311,7 @@ public class CallJabberImpl contentRequest.addChannel(remoteChannelRequest); } - XMPPConnection connection = protocolProvider.getConnection(); + Connection connection = protocolProvider.getConnection(); PacketCollector packetCollector = connection.createPacketCollector( new PacketIDFilter(conferenceRequest.getPacketID())); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java index 64eb5fa..c257202 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,1647 +15,1644 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.lang.reflect.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.ContentPacketExtension.SendersEnum;
-import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.service.neomedia.*;
-import org.jivesoftware.smack.*;
-import org.jivesoftware.smack.filter.*;
-import org.jivesoftware.smack.packet.*;
-import org.jivesoftware.smack.util.*;
-import org.jivesoftware.smackx.packet.*;
-
-/**
- * Implements a Jabber <tt>CallPeer</tt>.
- *
- * @author Emil Ivov
- * @author Lyubomir Marinov
- * @author Boris Grozev
- */
-public class CallPeerJabberImpl
- extends AbstractCallPeerJabberGTalkImpl
- <CallJabberImpl, CallPeerMediaHandlerJabberImpl, JingleIQ>
-{
- /**
- * The <tt>Logger</tt> used by the <tt>CallPeerJabberImpl</tt> class and its
- * instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(CallPeerJabberImpl.class);
-
- /**
- * If the call is cancelled before session-initiate is sent.
- */
- private boolean cancelled = false;
-
- /**
- * Synchronization object for candidates available.
- */
- private final Object candSyncRoot = new Object();
-
- /**
- * If the content-add does not contains candidates.
- */
- private boolean contentAddWithNoCands = false;
-
- /**
- * If we have processed the session initiate.
- */
- private boolean sessionInitiateProcessed = false;
-
- /**
- * Synchronization object. Synchronization object? Wow, who would have
- * thought! ;) Would be great to have a word on what we are syncing with it
- */
- private final Object sessionInitiateSyncRoot = new Object();
-
- /**
- * Synchronization object for SID.
- */
- private final Object sidSyncRoot = new Object();
-
- /**
- * The current value of the 'senders' field of the audio content in the
- * Jingle session with this <tt>CallPeer</tt>.
- * <tt>null</tt> should be interpreted as 'both', which is the default in
- * Jingle if the XML attribute is missing.
- */
- private SendersEnum audioSenders = SendersEnum.none;
-
- /**
- * The current value of the 'senders' field of the video content in the
- * Jingle session with this <tt>CallPeer</tt>.
- * <tt>null</tt> should be interpreted as 'both', which is the default in
- * Jingle if the XML attribute is missing.
- */
- private SendersEnum videoSenders = SendersEnum.none;
-
- /**
- * Creates a new call peer with address <tt>peerAddress</tt>.
- *
- * @param peerAddress the Jabber address of the new call peer.
- * @param owningCall the call that contains this call peer.
- */
- public CallPeerJabberImpl(String peerAddress,
- CallJabberImpl owningCall)
- {
- super(peerAddress, owningCall);
-
- setMediaHandler(new CallPeerMediaHandlerJabberImpl(this));
- }
-
- /**
- * Creates a new call peer with address <tt>peerAddress</tt>.
- *
- * @param peerAddress the Jabber address of the new call peer.
- * @param owningCall the call that contains this call peer.
- * @param sessionIQ The session-initiate <tt>JingleIQ</tt> which was
- * received from <tt>peerAddress</tt> and caused the creation of this
- * <tt>CallPeerJabberImpl</tt>
- */
- public CallPeerJabberImpl(String peerAddress,
- CallJabberImpl owningCall,
- JingleIQ sessionIQ)
- {
- this(peerAddress, owningCall);
- this.sessionInitIQ = sessionIQ;
- }
-
- /**
- * Send a session-accept <tt>JingleIQ</tt> to this <tt>CallPeer</tt>
- * @throws OperationFailedException if we fail to create or send the
- * response.
- */
- public synchronized void answer()
- throws OperationFailedException
- {
- Iterable<ContentPacketExtension> answer;
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- try
- {
- mediaHandler
- .getTransportManager()
- .wrapupConnectivityEstablishment();
- answer = mediaHandler.generateSessionAccept();
- for (ContentPacketExtension c : answer)
- setSenders(getMediaType(c), c.getSenders());
- }
- catch(Exception exc)
- {
- logger.info("Failed to answer an incoming call", exc);
-
- //send an error response
- String reasonText = "Error: " + exc.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- Reason.FAILED_APPLICATION,
- reasonText);
-
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- JingleIQ response
- = JinglePacketFactory.createSessionAccept(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- getSID(),
- answer);
-
- //send the packet first and start the stream later in case the media
- //relay needs to see it before letting hole punching techniques through.
- getProtocolProvider().getConnection().sendPacket(response);
-
- try
- {
- mediaHandler.start();
- }
- catch(UndeclaredThrowableException e)
- {
- Throwable exc = e.getUndeclaredThrowable();
-
- logger.info("Failed to establish a connection", exc);
-
- //send an error response
- String reasonText = "Error: " + exc.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- Reason.GENERAL_ERROR,
- reasonText);
-
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- //tell everyone we are connected so that the audio notifications would
- //stop
- setState(CallPeerState.CONNECTED);
- }
-
- /**
- * Returns the session ID of the Jingle session associated with this call.
- *
- * @return the session ID of the Jingle session associated with this call.
- */
- @Override
- public String getSID()
- {
- return sessionInitIQ != null ? sessionInitIQ.getSID() : null;
- }
-
- /**
- * Returns the IQ ID of the Jingle session-initiate packet associated with
- * this call.
- *
- * @return the IQ ID of the Jingle session-initiate packet associated with
- * this call.
- */
- public JingleIQ getSessionIQ()
- {
- return sessionInitIQ;
- }
-
- /**
- * Ends the call with this <tt>CallPeer</tt>. Depending on the state
- * of the peer the method would send a CANCEL, BYE, or BUSY_HERE message
- * and set the new state to DISCONNECTED.
- *
- * @param failed indicates if the hangup is following to a call failure or
- * simply a disconnect
- * @param reasonText the text, if any, to be set on the
- * <tt>ReasonPacketExtension</tt> as the value of its
- * @param reasonOtherExtension the <tt>PacketExtension</tt>, if any, to be
- * set on the <tt>ReasonPacketExtension</tt> as the value of its
- * <tt>otherExtension</tt> property
- */
- public void hangup(boolean failed,
- String reasonText,
- PacketExtension reasonOtherExtension)
- {
- CallPeerState prevPeerState = getState();
-
- // do nothing if the call is already ended
- if (CallPeerState.DISCONNECTED.equals(prevPeerState)
- || CallPeerState.FAILED.equals(prevPeerState))
- {
- if (logger.isDebugEnabled())
- logger.debug("Ignoring a request to hangup a call peer "
- + "that is already DISCONNECTED");
- return;
- }
-
- setState(
- failed ? CallPeerState.FAILED : CallPeerState.DISCONNECTED,
- reasonText);
-
- JingleIQ responseIQ = null;
-
- if (prevPeerState.equals(CallPeerState.CONNECTED)
- || CallPeerState.isOnHold(prevPeerState))
- {
- responseIQ = JinglePacketFactory.createBye(
- getProtocolProvider().getOurJID(), peerJID, getSID());
- }
- else if (CallPeerState.CONNECTING.equals(prevPeerState)
- || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(prevPeerState)
- || CallPeerState.ALERTING_REMOTE_SIDE.equals(prevPeerState))
- {
- String jingleSID = getSID();
-
- if(jingleSID == null)
- {
- synchronized(sidSyncRoot)
- {
- // we cancelled the call too early because the jingleSID
- // is null (i.e. the session-initiate has not been created)
- // and no need to send the session-terminate
- cancelled = true;
- return;
- }
- }
-
- responseIQ = JinglePacketFactory.createCancel(
- getProtocolProvider().getOurJID(), peerJID, getSID());
- }
- else if (prevPeerState.equals(CallPeerState.INCOMING_CALL))
- {
- responseIQ = JinglePacketFactory.createBusy(
- getProtocolProvider().getOurJID(), peerJID, getSID());
- }
- else if (prevPeerState.equals(CallPeerState.BUSY)
- || prevPeerState.equals(CallPeerState.FAILED))
- {
- // For FAILED and BUSY we only need to update CALL_STATUS
- // as everything else has been done already.
- }
- else
- {
- logger.info("Could not determine call peer state!");
- }
-
- if (responseIQ != null)
- {
- if (reasonOtherExtension != null)
- {
- ReasonPacketExtension reason
- = (ReasonPacketExtension)
- responseIQ.getExtension(
- ReasonPacketExtension.ELEMENT_NAME,
- ReasonPacketExtension.NAMESPACE);
-
- if (reason != null)
- {
- reason.setOtherExtension(reasonOtherExtension);
- }
- else if(reasonOtherExtension instanceof ReasonPacketExtension)
- {
- responseIQ.setReason(
- (ReasonPacketExtension)reasonOtherExtension);
- }
- }
-
- getProtocolProvider().getConnection().sendPacket(responseIQ);
- }
- }
-
- /**
- * Creates and sends a session-initiate {@link JingleIQ}.
- *
- * @param sessionInitiateExtensions a collection of additional and optional
- * <tt>PacketExtension</tt>s to be added to the <tt>session-initiate</tt>
- * {@link JingleIQ} which is to initiate the session with this
- * <tt>CallPeerJabberImpl</tt>
- * @throws OperationFailedException exception
- */
- protected synchronized void initiateSession(
- Iterable<PacketExtension> sessionInitiateExtensions)
- throws OperationFailedException
- {
- initiator = false;
-
- //Create the media description that we'd like to send to the other side.
- List<ContentPacketExtension> offer
- = getMediaHandler().createContentList();
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
-
- synchronized(sidSyncRoot)
- {
- sessionInitIQ
- = JinglePacketFactory.createSessionInitiate(
- protocolProvider.getOurJID(),
- this.peerJID,
- JingleIQ.generateSID(),
- offer);
-
- if(cancelled)
- {
- // we cancelled the call too early so no need to send the
- // session-initiate to peer
- getMediaHandler().getTransportManager().close();
- return;
- }
- }
-
- if (sessionInitiateExtensions != null)
- {
- for (PacketExtension sessionInitiateExtension
- : sessionInitiateExtensions)
- {
- sessionInitIQ.addExtension(sessionInitiateExtension);
- }
- }
-
- protocolProvider.getConnection().sendPacket(sessionInitIQ);
- }
-
- /**
- * Notifies this instance that a specific <tt>ColibriConferenceIQ</tt> has
- * been received. This <tt>CallPeerJabberImpl</tt> uses the part of the
- * information provided in the specified <tt>conferenceIQ</tt> which
- * concerns it only.
- *
- * @param conferenceIQ the <tt>ColibriConferenceIQ</tt> which has been
- * received
- */
- void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ)
- {
- /*
- * CallPeerJabberImpl does not itself/directly know the specifics
- * related to the channels allocated on the Jitsi Videobridge server.
- * The channels contain transport and media-related information so
- * forward the notification to CallPeerMediaHandlerJabberImpl.
- */
- getMediaHandler().processColibriConferenceIQ(conferenceIQ);
- }
-
- /**
- * Processes the content-accept {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer has accepted
- */
- public void processContentAccept(JingleIQ content)
- {
- List<ContentPacketExtension> contents = content.getContentList();
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- try
- {
- mediaHandler
- .getTransportManager()
- .wrapupConnectivityEstablishment();
- mediaHandler.processAnswer(contents);
- for (ContentPacketExtension c : contents)
- setSenders(getMediaType(c), c.getSenders());
- }
- catch (Exception e)
- {
- logger.warn("Failed to process a content-accept", e);
-
- // Send an error response.
- String reason = "Error: " + e.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- getProtocolProvider().getOurJID(),
- peerJID,
- sessionInitIQ.getSID(),
- Reason.INCOMPATIBLE_PARAMETERS,
- reason);
-
- setState(CallPeerState.FAILED, reason);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- mediaHandler.start();
- }
-
- /**
- * Processes the content-add {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer wants to be added
- */
- public void processContentAdd(final JingleIQ content)
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- List<ContentPacketExtension> contents = content.getContentList();
- Iterable<ContentPacketExtension> answerContents;
- JingleIQ contentIQ;
- boolean noCands = false;
- MediaStream oldVideoStream = mediaHandler.getStream(MediaType.VIDEO);
-
- if(logger.isInfoEnabled())
- logger.info("Looking for candidates in content-add.");
- try
- {
- if(!contentAddWithNoCands)
- {
- mediaHandler.processOffer(contents);
-
- /*
- * Gingle transport will not put candidate in session-initiate
- * and content-add.
- */
- for(ContentPacketExtension c : contents)
- {
- if(JingleUtils.getFirstCandidate(c, 1) == null)
- {
- contentAddWithNoCands = true;
- noCands = true;
- }
- }
- }
-
- // if no candidates are present, launch a new Thread which will
- // process and wait for the connectivity establishment (otherwise
- // the existing thread will be blocked and thus cannot receive
- // transport-info with candidates
- if(noCands)
- {
- new Thread()
- {
- @Override
- public void run()
- {
- try
- {
- synchronized(candSyncRoot)
- {
- candSyncRoot.wait();
- }
- }
- catch(InterruptedException e)
- {
- }
-
- processContentAdd(content);
- contentAddWithNoCands = false;
- }
- }.start();
- if(logger.isInfoEnabled())
- logger.info("No candidates found in content-add, started "
- + "new thread.");
- return;
- }
-
- mediaHandler
- .getTransportManager()
- .wrapupConnectivityEstablishment();
- if(logger.isInfoEnabled())
- logger.info("Wrapping up connectivity establishment");
- answerContents = mediaHandler.generateSessionAccept();
- contentIQ = null;
- }
- catch(Exception e)
- {
- logger.warn("Exception occurred", e);
-
- answerContents = null;
- contentIQ
- = JinglePacketFactory.createContentReject(
- getProtocolProvider().getOurJID(),
- this.peerJID,
- getSID(),
- answerContents);
- }
-
- if(contentIQ == null)
- {
- /* send content-accept */
- contentIQ
- = JinglePacketFactory.createContentAccept(
- getProtocolProvider().getOurJID(),
- this.peerJID,
- getSID(),
- answerContents);
- for (ContentPacketExtension c : answerContents)
- setSenders(getMediaType(c), c.getSenders());
- }
-
- getProtocolProvider().getConnection().sendPacket(contentIQ);
- mediaHandler.start();
-
- /*
- * If a remote peer turns her video on in a conference which is hosted
- * by the local peer and the local peer is not streaming her local
- * video, reinvite the other remote peers to enable RTP translation.
- */
- if (oldVideoStream == null)
- {
- MediaStream newVideoStream
- = mediaHandler.getStream(MediaType.VIDEO);
-
- if ((newVideoStream != null)
- && mediaHandler.isRTPTranslationEnabled(MediaType.VIDEO))
- {
- try
- {
- getCall().modifyVideoContent();
- }
- catch (OperationFailedException ofe)
- {
- logger.error("Failed to enable RTP translation", ofe);
- }
- }
- }
- }
-
- /**
- * Processes the content-modify {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer wants to be modified
- */
- public void processContentModify(JingleIQ content)
- {
- ContentPacketExtension ext = content.getContentList().get(0);
- MediaType mediaType = getMediaType(ext);
-
- try
- {
- boolean modify
- = (ext.getFirstChildOfType(RtpDescriptionPacketExtension.class)
- != null);
-
- getMediaHandler().reinitContent(ext.getName(), ext, modify);
-
- setSenders(mediaType, ext.getSenders());
-
- if (MediaType.VIDEO.equals(mediaType))
- getCall().modifyVideoContent();
- }
- catch(Exception e)
- {
- logger.info("Failed to process an incoming content-modify", e);
-
- // Send an error response.
- String reason = "Error: " + e.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- getProtocolProvider().getOurJID(),
- peerJID,
- sessionInitIQ.getSID(),
- Reason.INCOMPATIBLE_PARAMETERS,
- reason);
-
- setState(CallPeerState.FAILED, reason);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
- }
-
- /**
- * Processes the content-reject {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ}
- */
- public void processContentReject(JingleIQ content)
- {
- if(content.getContentList().isEmpty())
- {
- //send an error response;
- JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
- "Error: content rejected");
-
- setState(CallPeerState.FAILED, "Error: content rejected");
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
- }
-
- /**
- * Processes the content-remove {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer wants to be removed
- */
- public void processContentRemove(JingleIQ content)
- {
- List<ContentPacketExtension> contents = content.getContentList();
- boolean videoContentRemoved = false;
-
- if (!contents.isEmpty())
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- for(ContentPacketExtension c : contents)
- {
- mediaHandler.removeContent(c.getName());
-
- MediaType mediaType = getMediaType(c);
- setSenders(mediaType, SendersEnum.none);
-
- if (MediaType.VIDEO.equals(mediaType))
- videoContentRemoved = true;
- }
-
- /*
- * TODO XEP-0166: Jingle says: If the content-remove results in zero
- * content definitions for the session, the entity that receives the
- * content-remove SHOULD send a session-terminate action to the
- * other party (since a session with no content definitions is
- * void).
- */
- }
-
- if (videoContentRemoved)
- {
- // removing of the video content might affect the other sessions
- // in the call
- try
- {
- getCall().modifyVideoContent();
- }
- catch (Exception e)
- {
- logger.warn("Failed to update Jingle sessions");
- }
- }
- }
-
- /**
- * Processes a session-accept {@link JingleIQ}.
- *
- * @param sessionInitIQ The session-accept {@link JingleIQ} to process.
- */
- public void processSessionAccept(JingleIQ sessionInitIQ)
- {
- this.sessionInitIQ = sessionInitIQ;
-
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- List<ContentPacketExtension> answer = sessionInitIQ.getContentList();
-
- try
- {
- mediaHandler
- .getTransportManager()
- .wrapupConnectivityEstablishment();
- mediaHandler.processAnswer(answer);
- for (ContentPacketExtension c : answer)
- setSenders(getMediaType(c), c.getSenders());
- }
- catch(Exception exc)
- {
- if (logger.isInfoEnabled())
- logger.info("Failed to process a session-accept", exc);
-
- //send an error response;
- JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
- exc.getClass().getName() + ": " + exc.getMessage());
-
- setState(CallPeerState.FAILED, "Error: " + exc.getMessage());
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- //tell everyone we are connected so that the audio notifications would
- //stop
- setState(CallPeerState.CONNECTED);
-
- mediaHandler.start();
-
- /*
- * If video was added to the call after we sent the session-initiate
- * to this peer, it needs to be added to this peer's session with a
- * content-add.
- */
- sendModifyVideoContent();
- }
-
- /**
- * Handles the specified session <tt>info</tt> packet according to its
- * content.
- *
- * @param info the {@link SessionInfoPacketExtension} that we just received.
- */
- public void processSessionInfo(SessionInfoPacketExtension info)
- {
- switch (info.getType())
- {
- case ringing:
- setState(CallPeerState.ALERTING_REMOTE_SIDE);
- break;
- case hold:
- getMediaHandler().setRemotelyOnHold(true);
- reevalRemoteHoldStatus();
- break;
- case unhold:
- case active:
- getMediaHandler().setRemotelyOnHold(false);
- reevalRemoteHoldStatus();
- break;
- default:
- logger.warn("Received SessionInfoPacketExtension of unknown type");
- }
- }
-
- /**
- * Processes the session initiation {@link JingleIQ} that we were created
- * with, passing its content to the media handler and then sends either a
- * "session-info/ringing" or a "session-terminate" response.
- *
- * @param sessionInitIQ The {@link JingleIQ} that created the session that
- * we are handling here.
- */
- protected synchronized void processSessionInitiate(JingleIQ sessionInitIQ)
- {
- // Do initiate the session.
- this.sessionInitIQ = sessionInitIQ;
- this.initiator = true;
-
- // This is the SDP offer that came from the initial session-initiate.
- // Contrary to SIP, we are guaranteed to have content because XEP-0166
- // says: "A session consists of at least one content type at a time."
- List<ContentPacketExtension> offer = sessionInitIQ.getContentList();
-
- try
- {
- getMediaHandler().processOffer(offer);
-
- CoinPacketExtension coin = null;
-
- for(PacketExtension ext : sessionInitIQ.getExtensions())
- {
- if(ext.getElementName().equals(
- CoinPacketExtension.ELEMENT_NAME))
- {
- coin = (CoinPacketExtension)ext;
- break;
- }
- }
-
- /* does the call peer acts as a conference focus ? */
- if(coin != null)
- {
- setConferenceFocus(Boolean.parseBoolean(
- (String)coin.getAttribute("isfocus")));
- }
- }
- catch(Exception ex)
- {
- logger.info("Failed to process an incoming session initiate", ex);
-
- //send an error response;
- String reasonText = "Error: " + ex.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- Reason.INCOMPATIBLE_PARAMETERS,
- reasonText);
-
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- // If we do not get the info about the remote peer yet. Get it right
- // now.
- if(this.getDiscoveryInfo() == null)
- {
- String calleeURI = sessionInitIQ.getFrom();
- retrieveDiscoveryInfo(calleeURI);
- }
-
- //send a ringing response
- if (logger.isTraceEnabled())
- logger.trace("will send ringing response: ");
-
- getProtocolProvider().getConnection().sendPacket(
- JinglePacketFactory.createRinging(sessionInitIQ));
-
- synchronized(sessionInitiateSyncRoot)
- {
- sessionInitiateProcessed = true;
- sessionInitiateSyncRoot.notify();
- }
-
- //if this is a 3264 initiator, let's give them an early peek at our
- //answer so that they could start ICE (SIP-2-Jingle gateways won't
- //be able to send their candidates unless they have this)
- DiscoverInfo discoverInfo = getDiscoveryInfo();
- if ((discoverInfo != null)
- && discoverInfo.containsFeature(
- ProtocolProviderServiceJabberImpl.URN_IETF_RFC_3264))
- {
- getProtocolProvider().getConnection().sendPacket(
- JinglePacketFactory.createDescriptionInfo(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- getMediaHandler().getLocalContentList()));
- }
- }
-
- /**
- * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a
- * reason to the user, if there is one.
- *
- * @param jingleIQ the {@link JingleIQ} that's terminating our session.
- */
- public void processSessionTerminate(JingleIQ jingleIQ)
- {
- String reasonStr = "Call ended by remote side.";
- ReasonPacketExtension reasonExt = jingleIQ.getReason();
-
- if(reasonExt != null)
- {
- Reason reason = reasonExt.getReason();
-
- if(reason != null)
- reasonStr += " Reason: " + reason.toString() + ".";
-
- String text = reasonExt.getText();
-
- if(text != null)
- reasonStr += " " + text;
- }
-
- setState(CallPeerState.DISCONNECTED, reasonStr);
- }
-
- /**
- * Processes a specific "XEP-0251: Jingle Session Transfer"
- * <tt>transfer</tt> packet (extension).
- *
- * @param transfer the "XEP-0251: Jingle Session Transfer" transfer packet
- * (extension) to process
- * @throws OperationFailedException if anything goes wrong while processing
- * the specified <tt>transfer</tt> packet (extension)
- */
- public void processTransfer(TransferPacketExtension transfer)
- throws OperationFailedException
- {
- String attendantAddress = transfer.getFrom();
-
- if (attendantAddress == null)
- {
- throw new OperationFailedException(
- "Session transfer must contain a \'from\' attribute value.",
- OperationFailedException.ILLEGAL_ARGUMENT);
- }
-
- String calleeAddress = transfer.getTo();
-
- if (calleeAddress == null)
- {
- throw new OperationFailedException(
- "Session transfer must contain a \'to\' attribute value.",
- OperationFailedException.ILLEGAL_ARGUMENT);
- }
-
- // Checks if the transfer remote peer is contained by the roster of this
- // account.
- Roster roster = getProtocolProvider().getConnection().getRoster();
- if(!roster.contains(StringUtils.parseBareAddress(calleeAddress)))
- {
- String failedMessage =
- "Transfer impossible:\n"
- + "Account roster does not contain transfer peer: "
- + StringUtils.parseBareAddress(calleeAddress);
- setState(CallPeerState.FAILED, failedMessage);
- logger.info(failedMessage);
- }
-
- OperationSetBasicTelephonyJabberImpl basicTelephony
- = (OperationSetBasicTelephonyJabberImpl)
- getProtocolProvider()
- .getOperationSet(OperationSetBasicTelephony.class);
- CallJabberImpl calleeCall = new CallJabberImpl(basicTelephony);
- TransferPacketExtension calleeTransfer = new TransferPacketExtension();
- String sid = transfer.getSID();
-
- calleeTransfer.setFrom(attendantAddress);
- if (sid != null)
- {
- calleeTransfer.setSID(sid);
- calleeTransfer.setTo(calleeAddress);
- }
- basicTelephony.createOutgoingCall(
- calleeCall,
- calleeAddress,
- Arrays.asList(new PacketExtension[] { calleeTransfer }));
- }
-
- /**
- * Processes the <tt>transport-info</tt> {@link JingleIQ}.
- *
- * @param jingleIQ the <tt>transport-info</tt> {@link JingleIQ} to process
- */
- public void processTransportInfo(JingleIQ jingleIQ)
- {
- /*
- * The transport-info action is used to exchange transport candidates so
- * it only concerns the mediaHandler.
- */
- try
- {
- if(isInitiator())
- {
- synchronized(sessionInitiateSyncRoot)
- {
- if(!sessionInitiateProcessed)
- {
- try
- {
- sessionInitiateSyncRoot.wait();
- }
- catch(InterruptedException e)
- {
- }
- }
- }
- }
-
- getMediaHandler().processTransportInfo(
- jingleIQ.getContentList());
- }
- catch (OperationFailedException ofe)
- {
- logger.warn("Failed to process an incoming transport-info", ofe);
-
- //send an error response
- String reasonText = "Error: " + ofe.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- getProtocolProvider().getOurJID(),
- peerJID,
- sessionInitIQ.getSID(),
- Reason.GENERAL_ERROR,
- reasonText);
-
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
-
- return;
- }
-
- synchronized(candSyncRoot)
- {
- candSyncRoot.notify();
- }
- }
-
- /**
- * Puts the <tt>CallPeer</tt> represented by this instance on or off hold.
- *
- * @param onHold <tt>true</tt> to have the <tt>CallPeer</tt> put on hold;
- * <tt>false</tt>, otherwise
- *
- * @throws OperationFailedException if we fail to construct or send the
- * INVITE request putting the remote side on/off hold.
- */
- public void putOnHold(boolean onHold)
- throws OperationFailedException
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- mediaHandler.setLocallyOnHold(onHold);
-
- SessionInfoType type;
-
- if(onHold)
- type = SessionInfoType.hold;
- else
- {
- type = SessionInfoType.unhold;
- getMediaHandler().reinitAllContents();
- }
-
- //we are now on hold and need to realize this before potentially
- //spoiling it all with an exception while sending the packet :).
- reevalLocalHoldStatus();
-
- JingleIQ onHoldIQ = JinglePacketFactory.createSessionInfo(
- getProtocolProvider().getOurJID(),
- peerJID,
- getSID(),
- type);
-
- getProtocolProvider().getConnection().sendPacket(onHoldIQ);
- }
-
- /**
- * Send a <tt>content-add</tt> to add video setup.
- */
- private void sendAddVideoContent()
- {
- List<ContentPacketExtension> contents;
-
- try
- {
- contents = getMediaHandler().createContentList(MediaType.VIDEO);
- }
- catch(Exception exc)
- {
- logger.warn("Failed to gather content for video type", exc);
- return;
- }
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
- JingleIQ contentIQ
- = JinglePacketFactory.createContentAdd(
- protocolProvider.getOurJID(),
- this.peerJID,
- getSID(),
- contents);
-
- protocolProvider.getConnection().sendPacket(contentIQ);
- }
-
- /**
- * Sends a <tt>content</tt> message to reflect changes in the setup such as
- * the local peer/user becoming a conference focus.
- */
- public void sendCoinSessionInfo()
- {
- JingleIQ sessionInfoIQ
- = JinglePacketFactory.createSessionInfo(
- getProtocolProvider().getOurJID(),
- this.peerJID,
- getSID());
- CoinPacketExtension coinExt
- = new CoinPacketExtension(getCall().isConferenceFocus());
-
- sessionInfoIQ.addExtension(coinExt);
- getProtocolProvider().getConnection().sendPacket(sessionInfoIQ);
- }
-
- /**
- * Returns the <tt>MediaDirection</tt> that should be set for the content
- * of type <tt>mediaType</tt> in the Jingle session for this
- * <tt>CallPeer</tt>.
- * If we are the focus of a conference and are doing RTP translation,
- * takes into account the other <tt>CallPeer</tt>s in the <tt>Call</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> for which to return the
- * <tt>MediaDirection</tt>
- * @return the <tt>MediaDirection</tt> that should be used for the content
- * of type <tt>mediaType</tt> in the Jingle session for this
- * <tt>CallPeer</tt>.
- */
- private MediaDirection getDirectionForJingle(MediaType mediaType)
- {
- MediaDirection direction = MediaDirection.INACTIVE;
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- // If we are streaming media, the direction should allow sending
- if ( (MediaType.AUDIO == mediaType &&
- mediaHandler.isLocalAudioTransmissionEnabled()) ||
- (MediaType.VIDEO == mediaType &&
- isLocalVideoStreaming()))
- direction = direction.or(MediaDirection.SENDONLY);
-
- // If we are receiving media from this CallPeer, the direction should
- // allow receiving
- SendersEnum senders = getSenders(mediaType);
- if (senders == null || senders == SendersEnum.both ||
- (isInitiator() && senders == SendersEnum.initiator) ||
- (!isInitiator() && senders == SendersEnum.responder))
- direction = direction.or(MediaDirection.RECVONLY);
-
- // If we are the focus of a conference and we are receiving media from
- // another CallPeer in the same Call, the direction should allow sending
- CallJabberImpl call = getCall();
- if (call != null && call.isConferenceFocus())
- {
- for (CallPeerJabberImpl peer : call.getCallPeerList())
- {
- if (peer != this)
- {
- senders = peer.getSenders(mediaType);
- if (senders == null || senders == SendersEnum.both ||
- (peer.isInitiator()
- && senders == SendersEnum.initiator) ||
- (!peer.isInitiator()
- && senders == SendersEnum.responder))
- {
- direction = direction.or(MediaDirection.SENDONLY);
- break;
- }
- }
- }
- }
-
- return direction;
- }
-
- /**
- * Send, if necessary, a jingle <tt>content</tt> message to reflect change
- * in video setup. Whether the jingle session should have a video content,
- * and if so, the value of the <tt>senders</tt> field is determined
- * based on whether we are streaming local video and, if we are the focus
- * of a conference, on the other peers in the conference.
- * The message can be content-modify if video content exists (and the
- * <tt>senders</tt> field changes), content-add or content-remove.
- *
- * @return <tt>true</tt> if a jingle <tt>content</tt> message was sent.
- */
- public boolean sendModifyVideoContent()
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- MediaDirection direction = getDirectionForJingle(MediaType.VIDEO);
-
- ContentPacketExtension remoteContent
- = mediaHandler.getLocalContent(MediaType.VIDEO.toString());
-
- if (remoteContent == null)
- {
- if (direction == MediaDirection.INACTIVE)
- {
- // no video content, none needed
- return false;
- }
- else
- {
- if (getState() == CallPeerState.CONNECTED)
- {
- if (logger.isInfoEnabled())
- logger.info("Adding video content for " + this);
- sendAddVideoContent();
- return true;
- }
- return false;
- }
- }
- else
- {
- if (direction == MediaDirection.INACTIVE)
- {
- sendRemoveVideoContent();
- return true;
- }
- }
-
- SendersEnum senders = getSenders(MediaType.VIDEO);
- if (senders == null)
- senders = SendersEnum.both;
-
- SendersEnum newSenders = SendersEnum.none;
- if (MediaDirection.SENDRECV == direction)
- newSenders = SendersEnum.both;
- else if (MediaDirection.RECVONLY == direction)
- newSenders = isInitiator()
- ? SendersEnum.initiator : SendersEnum.responder;
- else if (MediaDirection.SENDONLY == direction)
- newSenders = isInitiator()
- ? SendersEnum.responder : SendersEnum.initiator;
-
- /*
- * Send Content-Modify
- */
- ContentPacketExtension ext = new ContentPacketExtension();
- String remoteContentName = remoteContent.getName();
-
- ext.setSenders(newSenders);
- ext.setCreator(remoteContent.getCreator());
- ext.setName(remoteContentName);
-
- if (newSenders != senders)
- {
- if (logger.isInfoEnabled())
- logger.info("Sending content modify, senders: "
- + senders + "->" + newSenders);
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
- JingleIQ contentIQ
- = JinglePacketFactory.createContentModify(
- protocolProvider.getOurJID(),
- this.peerJID,
- getSID(),
- ext);
-
- protocolProvider.getConnection().sendPacket(contentIQ);
- }
-
- try
- {
- mediaHandler.reinitContent(remoteContentName, ext, false);
- mediaHandler.start();
- }
- catch(Exception e)
- {
- logger.warn("Exception occurred during media reinitialization", e);
- }
-
- return (newSenders != senders);
- }
-
- /**
- * Send a <tt>content</tt> message to reflect change in video setup (start
- * or stop).
- */
- public void sendModifyVideoResolutionContent()
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- ContentPacketExtension remoteContent
- = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
- ContentPacketExtension content;
-
- logger.info("send modify-content to change resolution");
-
- // send content-modify with RTP description
-
- // create content list with resolution
- try
- {
- content = mediaHandler.createContentForMedia(MediaType.VIDEO);
- }
- catch (Exception e)
- {
- logger.warn("Failed to gather content for video type", e);
- return;
- }
-
- // if we are only receiving video senders is null
- SendersEnum senders = remoteContent.getSenders();
-
- if (senders != null)
- content.setSenders(senders);
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
- JingleIQ contentIQ
- = JinglePacketFactory.createContentModify(
- protocolProvider.getOurJID(),
- this.peerJID,
- getSID(),
- content);
-
- protocolProvider.getConnection().sendPacket(contentIQ);
-
- try
- {
- mediaHandler.reinitContent(remoteContent.getName(), content, false);
- mediaHandler.start();
- }
- catch(Exception e)
- {
- logger.warn("Exception occurred when media reinitialization", e);
- }
- }
-
- /**
- * Send a <tt>content-remove</tt> to remove video setup.
- */
- private void sendRemoveVideoContent()
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- ContentPacketExtension content = new ContentPacketExtension();
- ContentPacketExtension remoteContent
- = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
- if (remoteContent == null)
- return;
- String remoteContentName = remoteContent.getName();
-
- content.setName(remoteContentName);
- content.setCreator(remoteContent.getCreator());
- content.setSenders(remoteContent.getSenders());
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
- JingleIQ contentIQ
- = JinglePacketFactory.createContentRemove(
- protocolProvider.getOurJID(),
- this.peerJID,
- getSID(),
- Arrays.asList(content));
-
- protocolProvider.getConnection().sendPacket(contentIQ);
- mediaHandler.removeContent(remoteContentName);
- setSenders(MediaType.VIDEO, SendersEnum.none);
- }
-
- /**
- * Sends local candidate addresses from the local peer to the remote peer
- * using the <tt>transport-info</tt> {@link JingleIQ}.
- *
- * @param contents the local candidate addresses to be sent from the local
- * peer to the remote peer using the <tt>transport-info</tt>
- * {@link JingleIQ}
- */
- protected void sendTransportInfo(Iterable<ContentPacketExtension> contents)
- {
- // if the call is canceled, do not start sending candidates in
- // transport-info
- if(cancelled)
- return;
-
- JingleIQ transportInfo = new JingleIQ();
-
- for (ContentPacketExtension content : contents)
- transportInfo.addContent(content);
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
-
- transportInfo.setAction(JingleAction.TRANSPORT_INFO);
- transportInfo.setFrom(protocolProvider.getOurJID());
- transportInfo.setSID(getSID());
- transportInfo.setTo(getAddress());
- transportInfo.setType(IQ.Type.SET);
-
- PacketCollector collector
- = protocolProvider.getConnection().createPacketCollector(
- new PacketIDFilter(transportInfo.getPacketID()));
-
- protocolProvider.getConnection().sendPacket(transportInfo);
- collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
- collector.cancel();
- }
-
- @Override
- public void setState(CallPeerState newState, String reason, int reasonCode)
- {
- CallPeerState oldState = getState();
- try
- {
- /*
- * We need to dispose of the transport manager before the
- * 'call' field is set to null, because if Jitsi Videobridge is in
- * use, it (the call) is needed in order to expire the
- * Videobridge channels.
- */
- if (CallPeerState.DISCONNECTED.equals(newState)
- || CallPeerState.FAILED.equals(newState))
- getMediaHandler().getTransportManager().close();
- }
- finally
- {
- super.setState(newState, reason, reasonCode);
- }
-
- if (CallPeerState.isOnHold(oldState)
- && CallPeerState.CONNECTED.equals(newState))
- {
- try
- {
- getCall().modifyVideoContent();
- }
- catch (OperationFailedException ofe)
- {
- logger.error("Failed to update call video state after " +
- "'hold' status removed for "+this);
- }
- }
- }
-
- /**
- * Transfer (in the sense of call transfer) this <tt>CallPeer</tt> to a
- * specific callee address which may optionally be participating in an
- * active <tt>Call</tt>.
- *
- * @param to the address of the callee to transfer this <tt>CallPeer</tt> to
- * @param sid the Jingle session ID of the active <tt>Call</tt> between the
- * local peer and the callee in the case of attended transfer; <tt>null</tt>
- * in the case of unattended transfer
- * @throws OperationFailedException if something goes wrong
- */
- protected void transfer(String to, String sid)
- throws OperationFailedException
- {
- JingleIQ transferSessionInfo = new JingleIQ();
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
-
- transferSessionInfo.setAction(JingleAction.SESSION_INFO);
- transferSessionInfo.setFrom(protocolProvider.getOurJID());
- transferSessionInfo.setSID(getSID());
- transferSessionInfo.setTo(getAddress());
- transferSessionInfo.setType(IQ.Type.SET);
-
- TransferPacketExtension transfer = new TransferPacketExtension();
-
- // Attended transfer.
- if (sid != null)
- {
- /*
- * Not really sure what the value of the "from" attribute of the
- * "transfer" element should be but the examples in "XEP-0251:
- * Jingle Session Transfer" has it in the case of attended transfer.
- */
- transfer.setFrom(protocolProvider.getOurJID());
- transfer.setSID(sid);
-
- // Puts on hold the 2 calls before making the attended transfer.
- OperationSetBasicTelephonyJabberImpl basicTelephony
- = (OperationSetBasicTelephonyJabberImpl)
- protocolProvider.getOperationSet(
- OperationSetBasicTelephony.class);
- CallPeerJabberImpl callPeer = basicTelephony.getActiveCallPeer(sid);
- if(callPeer != null)
- {
- if(!CallPeerState.isOnHold(callPeer.getState()))
- {
- callPeer.putOnHold(true);
- }
- }
-
- if(!CallPeerState.isOnHold(this.getState()))
- {
- this.putOnHold(true);
- }
- }
- transfer.setTo(to);
-
- transferSessionInfo.addExtension(transfer);
-
- Connection connection = protocolProvider.getConnection();
- PacketCollector collector = connection.createPacketCollector(
- new PacketIDFilter(transferSessionInfo.getPacketID()));
- protocolProvider.getConnection().sendPacket(transferSessionInfo);
-
- Packet result
- = collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
-
- if(result == null)
- {
- // Log the failed transfer call and notify the user.
- throw new OperationFailedException(
- "No response to the \"transfer\" request.",
- OperationFailedException.ILLEGAL_ARGUMENT);
- }
- else if (((IQ) result).getType() != IQ.Type.RESULT)
- {
- // Log the failed transfer call and notify the user.
- throw new OperationFailedException(
- "Remote peer does not manage call \"transfer\"."
- + "Response to the \"transfer\" request is: "
- + ((IQ) result).getType(),
- OperationFailedException.ILLEGAL_ARGUMENT);
- }
- else
- {
- String message = ((sid == null) ? "Unattended" : "Attended")
- + " transfer to: "
- + to;
- // Implements the SIP behavior: once the transfer is accepted, the
- // current call is closed.
- hangup(
- false,
- message,
- new ReasonPacketExtension(Reason.SUCCESS,
- message,
- new TransferredPacketExtension()));
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public String getEntity()
- {
- return getAddress();
- }
-
- /**
- * {@inheritDoc}
- *
- * In Jingle there isn't an actual "direction" parameter. We use the
- * <tt>senders</tt> field to calculate the direction.
- */
- @Override
- public MediaDirection getDirection(MediaType mediaType)
- {
- SendersEnum senders = getSenders(mediaType);
-
- if (senders == SendersEnum.none)
- {
- return MediaDirection.INACTIVE;
- }
- else if (senders == null || senders == SendersEnum.both)
- {
- return MediaDirection.SENDRECV;
- }
- else if (senders == SendersEnum.initiator)
- {
- return
- isInitiator()
- ? MediaDirection.RECVONLY
- : MediaDirection.SENDONLY;
- }
- else //senders == SendersEnum.responder
- {
- return
- isInitiator()
- ? MediaDirection.SENDONLY
- : MediaDirection.RECVONLY;
- }
- }
-
- /**
- * Gets the current value of the <tt>senders</tt> field of the content with
- * name <tt>mediaType</tt> in the Jingle session with this
- * <tt>CallPeer</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> for which to get the current
- * value of the <tt>senders</tt> field.
- * @return the current value of the <tt>senders</tt> field of the content
- * with name <tt>mediaType</tt> in the Jingle session with this
- * <tt>CallPeer</tt>.
- */
- public SendersEnum getSenders(MediaType mediaType)
- {
- switch (mediaType)
- {
- case AUDIO:
- return audioSenders;
- case DATA:
- /*
- * FIXME DATA has been introduced as a MediaType but explicit
- * support for DATA content has not been added yet.
- */
- return SendersEnum.none;
- case VIDEO:
- return videoSenders;
- default:
- throw new IllegalArgumentException("mediaType");
- }
- }
-
- /**
- * Set the current value of the <tt>senders</tt> field of the content with
- * name <tt>mediaType</tt> in the Jingle session with this <tt>CallPeer</tt>
- * @param mediaType the <tt>MediaType</tt> for which to get the current
- * value of the <tt>senders</tt> field.
- * @param senders the value to set
- */
- public void setSenders(MediaType mediaType, SendersEnum senders)
- {
- if (mediaType == null)
- return;
- else if (MediaType.AUDIO.equals(mediaType))
- this.audioSenders = senders;
- else if (MediaType.VIDEO.equals(mediaType))
- this.videoSenders = senders;
- else
- throw new IllegalArgumentException("mediaType");
- }
-
- /**
- * Gets the <tt>MediaType</tt> of <tt>content</tt>. If <tt>content</tt>
- * does not have a <tt>description</tt> child and therefore not
- * <tt>MediaType</tt> can be associated with it, tries to take the
- * <tt>MediaType</tt> from the session's already established contents with
- * the same name as <tt>content</tt>
- * @param content the <tt>ContentPacketExtention</tt> for which to get the
- * <tt>MediaType</tt>
- * @return the <tt>MediaType</tt> of <tt>content</tt>.
- */
- public MediaType getMediaType(ContentPacketExtension content)
- {
- String contentName = content.getName();
- if (contentName == null)
- return null;
-
- MediaType mediaType = JingleUtils.getMediaType(content);
- if (mediaType == null)
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- for (MediaType m : MediaType.values())
- {
- ContentPacketExtension sessionContent
- = mediaHandler.getRemoteContent(m.toString());
- if (sessionContent == null)
- sessionContent = mediaHandler.getLocalContent(m.toString());
-
- if (sessionContent != null
- && contentName.equals(sessionContent.getName()))
- {
- mediaType = m;
- break;
- }
- }
- }
-
- return mediaType;
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber; + +import java.lang.reflect.*; +import java.util.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.ContentPacketExtension.SendersEnum; +import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; + +import org.jitsi.service.neomedia.*; +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.util.*; +import org.jivesoftware.smackx.packet.*; + +/** + * Implements a Jabber <tt>CallPeer</tt>. + * + * @author Emil Ivov + * @author Lyubomir Marinov + * @author Boris Grozev + */ +public class CallPeerJabberImpl + extends AbstractCallPeerJabberGTalkImpl + <CallJabberImpl, CallPeerMediaHandlerJabberImpl, JingleIQ> +{ + /** + * The <tt>Logger</tt> used by the <tt>CallPeerJabberImpl</tt> class and its + * instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(CallPeerJabberImpl.class); + + /** + * If the call is cancelled before session-initiate is sent. + */ + private boolean cancelled = false; + + /** + * Synchronization object for candidates available. + */ + private final Object candSyncRoot = new Object(); + + /** + * If the content-add does not contains candidates. + */ + private boolean contentAddWithNoCands = false; + + /** + * If we have processed the session initiate. + */ + private boolean sessionInitiateProcessed = false; + + /** + * Synchronization object. Synchronization object? Wow, who would have + * thought! ;) Would be great to have a word on what we are syncing with it + */ + private final Object sessionInitiateSyncRoot = new Object(); + + /** + * Synchronization object for SID. + */ + private final Object sidSyncRoot = new Object(); + + /** + * The current value of the 'senders' field of the audio content in the + * Jingle session with this <tt>CallPeer</tt>. + * <tt>null</tt> should be interpreted as 'both', which is the default in + * Jingle if the XML attribute is missing. + */ + private SendersEnum audioSenders = SendersEnum.none; + + /** + * The current value of the 'senders' field of the video content in the + * Jingle session with this <tt>CallPeer</tt>. + * <tt>null</tt> should be interpreted as 'both', which is the default in + * Jingle if the XML attribute is missing. + */ + private SendersEnum videoSenders = SendersEnum.none; + + /** + * Creates a new call peer with address <tt>peerAddress</tt>. + * + * @param peerAddress the Jabber address of the new call peer. + * @param owningCall the call that contains this call peer. + */ + public CallPeerJabberImpl(String peerAddress, + CallJabberImpl owningCall) + { + super(peerAddress, owningCall); + + setMediaHandler(new CallPeerMediaHandlerJabberImpl(this)); + } + + /** + * Creates a new call peer with address <tt>peerAddress</tt>. + * + * @param peerAddress the Jabber address of the new call peer. + * @param owningCall the call that contains this call peer. + * @param sessionIQ The session-initiate <tt>JingleIQ</tt> which was + * received from <tt>peerAddress</tt> and caused the creation of this + * <tt>CallPeerJabberImpl</tt> + */ + public CallPeerJabberImpl(String peerAddress, + CallJabberImpl owningCall, + JingleIQ sessionIQ) + { + this(peerAddress, owningCall); + this.sessionInitIQ = sessionIQ; + } + + /** + * Send a session-accept <tt>JingleIQ</tt> to this <tt>CallPeer</tt> + * @throws OperationFailedException if we fail to create or send the + * response. + */ + public synchronized void answer() + throws OperationFailedException + { + Iterable<ContentPacketExtension> answer; + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + + try + { + mediaHandler + .getTransportManager() + .wrapupConnectivityEstablishment(); + answer = mediaHandler.generateSessionAccept(); + for (ContentPacketExtension c : answer) + setSenders(getMediaType(c), c.getSenders()); + } + catch(Exception exc) + { + logger.info("Failed to answer an incoming call", exc); + + //send an error response + String reasonText = "Error: " + exc.getMessage(); + JingleIQ errResp + = JinglePacketFactory.createSessionTerminate( + sessionInitIQ.getTo(), + sessionInitIQ.getFrom(), + sessionInitIQ.getSID(), + Reason.FAILED_APPLICATION, + reasonText); + + setState(CallPeerState.FAILED, reasonText); + getProtocolProvider().getConnection().sendPacket(errResp); + return; + } + + JingleIQ response + = JinglePacketFactory.createSessionAccept( + sessionInitIQ.getTo(), + sessionInitIQ.getFrom(), + getSID(), + answer); + + //send the packet first and start the stream later in case the media + //relay needs to see it before letting hole punching techniques through. + getProtocolProvider().getConnection().sendPacket(response); + + try + { + mediaHandler.start(); + } + catch(UndeclaredThrowableException e) + { + Throwable exc = e.getUndeclaredThrowable(); + + logger.info("Failed to establish a connection", exc); + + //send an error response + String reasonText = "Error: " + exc.getMessage(); + JingleIQ errResp + = JinglePacketFactory.createSessionTerminate( + sessionInitIQ.getTo(), + sessionInitIQ.getFrom(), + sessionInitIQ.getSID(), + Reason.GENERAL_ERROR, + reasonText); + + setState(CallPeerState.FAILED, reasonText); + getProtocolProvider().getConnection().sendPacket(errResp); + return; + } + + //tell everyone we are connected so that the audio notifications would + //stop + setState(CallPeerState.CONNECTED); + } + + /** + * Returns the session ID of the Jingle session associated with this call. + * + * @return the session ID of the Jingle session associated with this call. + */ + @Override + public String getSID() + { + return sessionInitIQ != null ? sessionInitIQ.getSID() : null; + } + + /** + * Returns the IQ ID of the Jingle session-initiate packet associated with + * this call. + * + * @return the IQ ID of the Jingle session-initiate packet associated with + * this call. + */ + public JingleIQ getSessionIQ() + { + return sessionInitIQ; + } + + /** + * Ends the call with this <tt>CallPeer</tt>. Depending on the state + * of the peer the method would send a CANCEL, BYE, or BUSY_HERE message + * and set the new state to DISCONNECTED. + * + * @param failed indicates if the hangup is following to a call failure or + * simply a disconnect + * @param reasonText the text, if any, to be set on the + * <tt>ReasonPacketExtension</tt> as the value of its + * @param reasonOtherExtension the <tt>PacketExtension</tt>, if any, to be + * set on the <tt>ReasonPacketExtension</tt> as the value of its + * <tt>otherExtension</tt> property + */ + public void hangup(boolean failed, + String reasonText, + PacketExtension reasonOtherExtension) + { + CallPeerState prevPeerState = getState(); + + // do nothing if the call is already ended + if (CallPeerState.DISCONNECTED.equals(prevPeerState) + || CallPeerState.FAILED.equals(prevPeerState)) + { + if (logger.isDebugEnabled()) + logger.debug("Ignoring a request to hangup a call peer " + + "that is already DISCONNECTED"); + return; + } + + setState( + failed ? CallPeerState.FAILED : CallPeerState.DISCONNECTED, + reasonText); + + JingleIQ responseIQ = null; + + if (prevPeerState.equals(CallPeerState.CONNECTED) + || CallPeerState.isOnHold(prevPeerState)) + { + responseIQ = JinglePacketFactory.createBye( + getProtocolProvider().getOurJID(), peerJID, getSID()); + } + else if (CallPeerState.CONNECTING.equals(prevPeerState) + || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(prevPeerState) + || CallPeerState.ALERTING_REMOTE_SIDE.equals(prevPeerState)) + { + String jingleSID = getSID(); + + if(jingleSID == null) + { + synchronized(sidSyncRoot) + { + // we cancelled the call too early because the jingleSID + // is null (i.e. the session-initiate has not been created) + // and no need to send the session-terminate + cancelled = true; + return; + } + } + + responseIQ = JinglePacketFactory.createCancel( + getProtocolProvider().getOurJID(), peerJID, getSID()); + } + else if (prevPeerState.equals(CallPeerState.INCOMING_CALL)) + { + responseIQ = JinglePacketFactory.createBusy( + getProtocolProvider().getOurJID(), peerJID, getSID()); + } + else if (prevPeerState.equals(CallPeerState.BUSY) + || prevPeerState.equals(CallPeerState.FAILED)) + { + // For FAILED and BUSY we only need to update CALL_STATUS + // as everything else has been done already. + } + else + { + logger.info("Could not determine call peer state!"); + } + + if (responseIQ != null) + { + if (reasonOtherExtension != null) + { + ReasonPacketExtension reason + = (ReasonPacketExtension) + responseIQ.getExtension( + ReasonPacketExtension.ELEMENT_NAME, + ReasonPacketExtension.NAMESPACE); + + if (reason != null) + { + reason.setOtherExtension(reasonOtherExtension); + } + else if(reasonOtherExtension instanceof ReasonPacketExtension) + { + responseIQ.setReason( + (ReasonPacketExtension)reasonOtherExtension); + } + } + + getProtocolProvider().getConnection().sendPacket(responseIQ); + } + } + + /** + * Creates and sends a session-initiate {@link JingleIQ}. + * + * @param sessionInitiateExtensions a collection of additional and optional + * <tt>PacketExtension</tt>s to be added to the <tt>session-initiate</tt> + * {@link JingleIQ} which is to initiate the session with this + * <tt>CallPeerJabberImpl</tt> + * @throws OperationFailedException exception + */ + protected synchronized void initiateSession( + Iterable<PacketExtension> sessionInitiateExtensions) + throws OperationFailedException + { + initiator = false; + + //Create the media description that we'd like to send to the other side. + List<ContentPacketExtension> offer + = getMediaHandler().createContentList(); + + ProtocolProviderServiceJabberImpl protocolProvider + = getProtocolProvider(); + + synchronized(sidSyncRoot) + { + sessionInitIQ + = JinglePacketFactory.createSessionInitiate( + protocolProvider.getOurJID(), + this.peerJID, + JingleIQ.generateSID(), + offer); + + if(cancelled) + { + // we cancelled the call too early so no need to send the + // session-initiate to peer + getMediaHandler().getTransportManager().close(); + return; + } + } + + if (sessionInitiateExtensions != null) + { + for (PacketExtension sessionInitiateExtension + : sessionInitiateExtensions) + { + sessionInitIQ.addExtension(sessionInitiateExtension); + } + } + + protocolProvider.getConnection().sendPacket(sessionInitIQ); + } + + /** + * Notifies this instance that a specific <tt>ColibriConferenceIQ</tt> has + * been received. This <tt>CallPeerJabberImpl</tt> uses the part of the + * information provided in the specified <tt>conferenceIQ</tt> which + * concerns it only. + * + * @param conferenceIQ the <tt>ColibriConferenceIQ</tt> which has been + * received + */ + void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ) + { + /* + * CallPeerJabberImpl does not itself/directly know the specifics + * related to the channels allocated on the Jitsi Videobridge server. + * The channels contain transport and media-related information so + * forward the notification to CallPeerMediaHandlerJabberImpl. + */ + getMediaHandler().processColibriConferenceIQ(conferenceIQ); + } + + /** + * Processes the content-accept {@link JingleIQ}. + * + * @param content The {@link JingleIQ} that contains content that remote + * peer has accepted + */ + public void processContentAccept(JingleIQ content) + { + List<ContentPacketExtension> contents = content.getContentList(); + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + + try + { + mediaHandler + .getTransportManager() + .wrapupConnectivityEstablishment(); + mediaHandler.processAnswer(contents); + for (ContentPacketExtension c : contents) + setSenders(getMediaType(c), c.getSenders()); + } + catch (Exception e) + { + logger.warn("Failed to process a content-accept", e); + + // Send an error response. + String reason = "Error: " + e.getMessage(); + JingleIQ errResp + = JinglePacketFactory.createSessionTerminate( + getProtocolProvider().getOurJID(), + peerJID, + sessionInitIQ.getSID(), + Reason.INCOMPATIBLE_PARAMETERS, + reason); + + setState(CallPeerState.FAILED, reason); + getProtocolProvider().getConnection().sendPacket(errResp); + return; + } + + mediaHandler.start(); + } + + /** + * Processes the content-add {@link JingleIQ}. + * + * @param content The {@link JingleIQ} that contains content that remote + * peer wants to be added + */ + public void processContentAdd(final JingleIQ content) + { + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + List<ContentPacketExtension> contents = content.getContentList(); + Iterable<ContentPacketExtension> answerContents; + JingleIQ contentIQ; + boolean noCands = false; + MediaStream oldVideoStream = mediaHandler.getStream(MediaType.VIDEO); + + if(logger.isInfoEnabled()) + logger.info("Looking for candidates in content-add."); + try + { + if(!contentAddWithNoCands) + { + mediaHandler.processOffer(contents); + + /* + * Gingle transport will not put candidate in session-initiate + * and content-add. + */ + for(ContentPacketExtension c : contents) + { + if(JingleUtils.getFirstCandidate(c, 1) == null) + { + contentAddWithNoCands = true; + noCands = true; + } + } + } + + // if no candidates are present, launch a new Thread which will + // process and wait for the connectivity establishment (otherwise + // the existing thread will be blocked and thus cannot receive + // transport-info with candidates + if(noCands) + { + new Thread() + { + @Override + public void run() + { + try + { + synchronized(candSyncRoot) + { + candSyncRoot.wait(); + } + } + catch(InterruptedException e) + { + } + + processContentAdd(content); + contentAddWithNoCands = false; + } + }.start(); + if(logger.isInfoEnabled()) + logger.info("No candidates found in content-add, started " + + "new thread."); + return; + } + + mediaHandler + .getTransportManager() + .wrapupConnectivityEstablishment(); + if(logger.isInfoEnabled()) + logger.info("Wrapping up connectivity establishment"); + answerContents = mediaHandler.generateSessionAccept(); + contentIQ = null; + } + catch(Exception e) + { + logger.warn("Exception occurred", e); + + answerContents = null; + contentIQ + = JinglePacketFactory.createContentReject( + getProtocolProvider().getOurJID(), + this.peerJID, + getSID(), + answerContents); + } + + if(contentIQ == null) + { + /* send content-accept */ + contentIQ + = JinglePacketFactory.createContentAccept( + getProtocolProvider().getOurJID(), + this.peerJID, + getSID(), + answerContents); + for (ContentPacketExtension c : answerContents) + setSenders(getMediaType(c), c.getSenders()); + } + + getProtocolProvider().getConnection().sendPacket(contentIQ); + mediaHandler.start(); + + /* + * If a remote peer turns her video on in a conference which is hosted + * by the local peer and the local peer is not streaming her local + * video, reinvite the other remote peers to enable RTP translation. + */ + if (oldVideoStream == null) + { + MediaStream newVideoStream + = mediaHandler.getStream(MediaType.VIDEO); + + if ((newVideoStream != null) + && mediaHandler.isRTPTranslationEnabled(MediaType.VIDEO)) + { + try + { + getCall().modifyVideoContent(); + } + catch (OperationFailedException ofe) + { + logger.error("Failed to enable RTP translation", ofe); + } + } + } + } + + /** + * Processes the content-modify {@link JingleIQ}. + * + * @param content The {@link JingleIQ} that contains content that remote + * peer wants to be modified + */ + public void processContentModify(JingleIQ content) + { + ContentPacketExtension ext = content.getContentList().get(0); + MediaType mediaType = getMediaType(ext); + + try + { + boolean modify + = (ext.getFirstChildOfType(RtpDescriptionPacketExtension.class) + != null); + + getMediaHandler().reinitContent(ext.getName(), ext, modify); + + setSenders(mediaType, ext.getSenders()); + + if (MediaType.VIDEO.equals(mediaType)) + getCall().modifyVideoContent(); + } + catch(Exception e) + { + logger.info("Failed to process an incoming content-modify", e); + + // Send an error response. + String reason = "Error: " + e.getMessage(); + JingleIQ errResp + = JinglePacketFactory.createSessionTerminate( + getProtocolProvider().getOurJID(), + peerJID, + sessionInitIQ.getSID(), + Reason.INCOMPATIBLE_PARAMETERS, + reason); + + setState(CallPeerState.FAILED, reason); + getProtocolProvider().getConnection().sendPacket(errResp); + return; + } + } + + /** + * Processes the content-reject {@link JingleIQ}. + * + * @param content The {@link JingleIQ} + */ + public void processContentReject(JingleIQ content) + { + if(content.getContentList().isEmpty()) + { + //send an error response; + JingleIQ errResp = JinglePacketFactory.createSessionTerminate( + sessionInitIQ.getTo(), sessionInitIQ.getFrom(), + sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS, + "Error: content rejected"); + + setState(CallPeerState.FAILED, "Error: content rejected"); + getProtocolProvider().getConnection().sendPacket(errResp); + return; + } + } + + /** + * Processes the content-remove {@link JingleIQ}. + * + * @param content The {@link JingleIQ} that contains content that remote + * peer wants to be removed + */ + public void processContentRemove(JingleIQ content) + { + List<ContentPacketExtension> contents = content.getContentList(); + boolean videoContentRemoved = false; + + if (!contents.isEmpty()) + { + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + + for(ContentPacketExtension c : contents) + { + mediaHandler.removeContent(c.getName()); + + MediaType mediaType = getMediaType(c); + setSenders(mediaType, SendersEnum.none); + + if (MediaType.VIDEO.equals(mediaType)) + videoContentRemoved = true; + } + + /* + * TODO XEP-0166: Jingle says: If the content-remove results in zero + * content definitions for the session, the entity that receives the + * content-remove SHOULD send a session-terminate action to the + * other party (since a session with no content definitions is + * void). + */ + } + + if (videoContentRemoved) + { + // removing of the video content might affect the other sessions + // in the call + try + { + getCall().modifyVideoContent(); + } + catch (Exception e) + { + logger.warn("Failed to update Jingle sessions"); + } + } + } + + /** + * Processes a session-accept {@link JingleIQ}. + * + * @param sessionInitIQ The session-accept {@link JingleIQ} to process. + */ + public void processSessionAccept(JingleIQ sessionInitIQ) + { + this.sessionInitIQ = sessionInitIQ; + + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + List<ContentPacketExtension> answer = sessionInitIQ.getContentList(); + + try + { + mediaHandler + .getTransportManager() + .wrapupConnectivityEstablishment(); + mediaHandler.processAnswer(answer); + for (ContentPacketExtension c : answer) + setSenders(getMediaType(c), c.getSenders()); + } + catch(Exception exc) + { + if (logger.isInfoEnabled()) + logger.info("Failed to process a session-accept", exc); + + //send an error response; + JingleIQ errResp = JinglePacketFactory.createSessionTerminate( + sessionInitIQ.getTo(), sessionInitIQ.getFrom(), + sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS, + exc.getClass().getName() + ": " + exc.getMessage()); + + setState(CallPeerState.FAILED, "Error: " + exc.getMessage()); + getProtocolProvider().getConnection().sendPacket(errResp); + return; + } + + //tell everyone we are connected so that the audio notifications would + //stop + setState(CallPeerState.CONNECTED); + + mediaHandler.start(); + + /* + * If video was added to the call after we sent the session-initiate + * to this peer, it needs to be added to this peer's session with a + * content-add. + */ + sendModifyVideoContent(); + } + + /** + * Handles the specified session <tt>info</tt> packet according to its + * content. + * + * @param info the {@link SessionInfoPacketExtension} that we just received. + */ + public void processSessionInfo(SessionInfoPacketExtension info) + { + switch (info.getType()) + { + case ringing: + setState(CallPeerState.ALERTING_REMOTE_SIDE); + break; + case hold: + getMediaHandler().setRemotelyOnHold(true); + reevalRemoteHoldStatus(); + break; + case unhold: + case active: + getMediaHandler().setRemotelyOnHold(false); + reevalRemoteHoldStatus(); + break; + default: + logger.warn("Received SessionInfoPacketExtension of unknown type"); + } + } + + /** + * Processes the session initiation {@link JingleIQ} that we were created + * with, passing its content to the media handler and then sends either a + * "session-info/ringing" or a "session-terminate" response. + * + * @param sessionInitIQ The {@link JingleIQ} that created the session that + * we are handling here. + */ + protected synchronized void processSessionInitiate(JingleIQ sessionInitIQ) + { + // Do initiate the session. + this.sessionInitIQ = sessionInitIQ; + this.initiator = true; + + // This is the SDP offer that came from the initial session-initiate. + // Contrary to SIP, we are guaranteed to have content because XEP-0166 + // says: "A session consists of at least one content type at a time." + List<ContentPacketExtension> offer = sessionInitIQ.getContentList(); + + try + { + getMediaHandler().processOffer(offer); + + CoinPacketExtension coin = null; + + for(PacketExtension ext : sessionInitIQ.getExtensions()) + { + if(ext.getElementName().equals( + CoinPacketExtension.ELEMENT_NAME)) + { + coin = (CoinPacketExtension)ext; + break; + } + } + + /* does the call peer acts as a conference focus ? */ + if(coin != null) + { + setConferenceFocus(Boolean.parseBoolean( + (String)coin.getAttribute("isfocus"))); + } + } + catch(Exception ex) + { + logger.info("Failed to process an incoming session initiate", ex); + + //send an error response; + String reasonText = "Error: " + ex.getMessage(); + JingleIQ errResp + = JinglePacketFactory.createSessionTerminate( + sessionInitIQ.getTo(), + sessionInitIQ.getFrom(), + sessionInitIQ.getSID(), + Reason.INCOMPATIBLE_PARAMETERS, + reasonText); + + setState(CallPeerState.FAILED, reasonText); + getProtocolProvider().getConnection().sendPacket(errResp); + return; + } + + // If we do not get the info about the remote peer yet. Get it right + // now. + if(this.getDiscoveryInfo() == null) + { + String calleeURI = sessionInitIQ.getFrom(); + retrieveDiscoveryInfo(calleeURI); + } + + //send a ringing response + if (logger.isTraceEnabled()) + logger.trace("will send ringing response: "); + + getProtocolProvider().getConnection().sendPacket( + JinglePacketFactory.createRinging(sessionInitIQ)); + + synchronized(sessionInitiateSyncRoot) + { + sessionInitiateProcessed = true; + sessionInitiateSyncRoot.notify(); + } + + //if this is a 3264 initiator, let's give them an early peek at our + //answer so that they could start ICE (SIP-2-Jingle gateways won't + //be able to send their candidates unless they have this) + DiscoverInfo discoverInfo = getDiscoveryInfo(); + if ((discoverInfo != null) + && discoverInfo.containsFeature( + ProtocolProviderServiceJabberImpl.URN_IETF_RFC_3264)) + { + getProtocolProvider().getConnection().sendPacket( + JinglePacketFactory.createDescriptionInfo( + sessionInitIQ.getTo(), + sessionInitIQ.getFrom(), + sessionInitIQ.getSID(), + getMediaHandler().getLocalContentList())); + } + } + + /** + * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a + * reason to the user, if there is one. + * + * @param jingleIQ the {@link JingleIQ} that's terminating our session. + */ + public void processSessionTerminate(JingleIQ jingleIQ) + { + String reasonStr = "Call ended by remote side."; + ReasonPacketExtension reasonExt = jingleIQ.getReason(); + + if(reasonExt != null) + { + Reason reason = reasonExt.getReason(); + + if(reason != null) + reasonStr += " Reason: " + reason.toString() + "."; + + String text = reasonExt.getText(); + + if(text != null) + reasonStr += " " + text; + } + + setState(CallPeerState.DISCONNECTED, reasonStr); + } + + /** + * Processes a specific "XEP-0251: Jingle Session Transfer" + * <tt>transfer</tt> packet (extension). + * + * @param transfer the "XEP-0251: Jingle Session Transfer" transfer packet + * (extension) to process + * @throws OperationFailedException if anything goes wrong while processing + * the specified <tt>transfer</tt> packet (extension) + */ + public void processTransfer(TransferPacketExtension transfer) + throws OperationFailedException + { + String attendantAddress = transfer.getFrom(); + + if (attendantAddress == null) + { + throw new OperationFailedException( + "Session transfer must contain a \'from\' attribute value.", + OperationFailedException.ILLEGAL_ARGUMENT); + } + + String calleeAddress = transfer.getTo(); + + if (calleeAddress == null) + { + throw new OperationFailedException( + "Session transfer must contain a \'to\' attribute value.", + OperationFailedException.ILLEGAL_ARGUMENT); + } + + // Checks if the transfer remote peer is contained by the roster of this + // account. + Roster roster = getProtocolProvider().getConnection().getRoster(); + if(!roster.contains(StringUtils.parseBareAddress(calleeAddress))) + { + String failedMessage = + "Transfer impossible:\n" + + "Account roster does not contain transfer peer: " + + StringUtils.parseBareAddress(calleeAddress); + setState(CallPeerState.FAILED, failedMessage); + logger.info(failedMessage); + } + + OperationSetBasicTelephonyJabberImpl basicTelephony + = (OperationSetBasicTelephonyJabberImpl) + getProtocolProvider() + .getOperationSet(OperationSetBasicTelephony.class); + CallJabberImpl calleeCall = new CallJabberImpl(basicTelephony); + TransferPacketExtension calleeTransfer = new TransferPacketExtension(); + String sid = transfer.getSID(); + + calleeTransfer.setFrom(attendantAddress); + if (sid != null) + { + calleeTransfer.setSID(sid); + calleeTransfer.setTo(calleeAddress); + } + basicTelephony.createOutgoingCall( + calleeCall, + calleeAddress, + Arrays.asList(new PacketExtension[] { calleeTransfer })); + } + + /** + * Processes the <tt>transport-info</tt> {@link JingleIQ}. + * + * @param jingleIQ the <tt>transport-info</tt> {@link JingleIQ} to process + */ + public void processTransportInfo(JingleIQ jingleIQ) + { + /* + * The transport-info action is used to exchange transport candidates so + * it only concerns the mediaHandler. + */ + try + { + if(isInitiator()) + { + synchronized(sessionInitiateSyncRoot) + { + if(!sessionInitiateProcessed) + { + try + { + sessionInitiateSyncRoot.wait(); + } + catch(InterruptedException e) + { + } + } + } + } + + getMediaHandler().processTransportInfo( + jingleIQ.getContentList()); + } + catch (OperationFailedException ofe) + { + logger.warn("Failed to process an incoming transport-info", ofe); + + //send an error response + String reasonText = "Error: " + ofe.getMessage(); + JingleIQ errResp + = JinglePacketFactory.createSessionTerminate( + getProtocolProvider().getOurJID(), + peerJID, + sessionInitIQ.getSID(), + Reason.GENERAL_ERROR, + reasonText); + + setState(CallPeerState.FAILED, reasonText); + getProtocolProvider().getConnection().sendPacket(errResp); + + return; + } + + synchronized(candSyncRoot) + { + candSyncRoot.notify(); + } + } + + /** + * Puts the <tt>CallPeer</tt> represented by this instance on or off hold. + * + * @param onHold <tt>true</tt> to have the <tt>CallPeer</tt> put on hold; + * <tt>false</tt>, otherwise + * + * @throws OperationFailedException if we fail to construct or send the + * INVITE request putting the remote side on/off hold. + */ + public void putOnHold(boolean onHold) + throws OperationFailedException + { + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + + mediaHandler.setLocallyOnHold(onHold); + + SessionInfoType type; + + if(onHold) + type = SessionInfoType.hold; + else + { + type = SessionInfoType.unhold; + getMediaHandler().reinitAllContents(); + } + + //we are now on hold and need to realize this before potentially + //spoiling it all with an exception while sending the packet :). + reevalLocalHoldStatus(); + + JingleIQ onHoldIQ = JinglePacketFactory.createSessionInfo( + getProtocolProvider().getOurJID(), + peerJID, + getSID(), + type); + + getProtocolProvider().getConnection().sendPacket(onHoldIQ); + } + + /** + * Send a <tt>content-add</tt> to add video setup. + */ + private void sendAddVideoContent() + { + List<ContentPacketExtension> contents; + + try + { + contents = getMediaHandler().createContentList(MediaType.VIDEO); + } + catch(Exception exc) + { + logger.warn("Failed to gather content for video type", exc); + return; + } + + ProtocolProviderServiceJabberImpl protocolProvider + = getProtocolProvider(); + JingleIQ contentIQ + = JinglePacketFactory.createContentAdd( + protocolProvider.getOurJID(), + this.peerJID, + getSID(), + contents); + + protocolProvider.getConnection().sendPacket(contentIQ); + } + + /** + * Sends a <tt>content</tt> message to reflect changes in the setup such as + * the local peer/user becoming a conference focus. + */ + public void sendCoinSessionInfo() + { + JingleIQ sessionInfoIQ + = JinglePacketFactory.createSessionInfo( + getProtocolProvider().getOurJID(), + this.peerJID, + getSID()); + CoinPacketExtension coinExt + = new CoinPacketExtension(getCall().isConferenceFocus()); + + sessionInfoIQ.addExtension(coinExt); + getProtocolProvider().getConnection().sendPacket(sessionInfoIQ); + } + + /** + * Returns the <tt>MediaDirection</tt> that should be set for the content + * of type <tt>mediaType</tt> in the Jingle session for this + * <tt>CallPeer</tt>. + * If we are the focus of a conference and are doing RTP translation, + * takes into account the other <tt>CallPeer</tt>s in the <tt>Call</tt>. + * + * @param mediaType the <tt>MediaType</tt> for which to return the + * <tt>MediaDirection</tt> + * @return the <tt>MediaDirection</tt> that should be used for the content + * of type <tt>mediaType</tt> in the Jingle session for this + * <tt>CallPeer</tt>. + */ + private MediaDirection getDirectionForJingle(MediaType mediaType) + { + MediaDirection direction = MediaDirection.INACTIVE; + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + + // If we are streaming media, the direction should allow sending + if ( (MediaType.AUDIO == mediaType && + mediaHandler.isLocalAudioTransmissionEnabled()) || + (MediaType.VIDEO == mediaType && + isLocalVideoStreaming())) + direction = direction.or(MediaDirection.SENDONLY); + + // If we are receiving media from this CallPeer, the direction should + // allow receiving + SendersEnum senders = getSenders(mediaType); + if (senders == null || senders == SendersEnum.both || + (isInitiator() && senders == SendersEnum.initiator) || + (!isInitiator() && senders == SendersEnum.responder)) + direction = direction.or(MediaDirection.RECVONLY); + + // If we are the focus of a conference and we are receiving media from + // another CallPeer in the same Call, the direction should allow sending + CallJabberImpl call = getCall(); + if (call != null && call.isConferenceFocus()) + { + for (CallPeerJabberImpl peer : call.getCallPeerList()) + { + if (peer != this) + { + senders = peer.getSenders(mediaType); + if (senders == null || senders == SendersEnum.both || + (peer.isInitiator() + && senders == SendersEnum.initiator) || + (!peer.isInitiator() + && senders == SendersEnum.responder)) + { + direction = direction.or(MediaDirection.SENDONLY); + break; + } + } + } + } + + return direction; + } + + /** + * Send, if necessary, a jingle <tt>content</tt> message to reflect change + * in video setup. Whether the jingle session should have a video content, + * and if so, the value of the <tt>senders</tt> field is determined + * based on whether we are streaming local video and, if we are the focus + * of a conference, on the other peers in the conference. + * The message can be content-modify if video content exists (and the + * <tt>senders</tt> field changes), content-add or content-remove. + * + * @return <tt>true</tt> if a jingle <tt>content</tt> message was sent. + */ + public boolean sendModifyVideoContent() + { + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + MediaDirection direction = getDirectionForJingle(MediaType.VIDEO); + + ContentPacketExtension remoteContent + = mediaHandler.getLocalContent(MediaType.VIDEO.toString()); + + if (remoteContent == null) + { + if (direction == MediaDirection.INACTIVE) + { + // no video content, none needed + return false; + } + else + { + if (getState() == CallPeerState.CONNECTED) + { + if (logger.isInfoEnabled()) + logger.info("Adding video content for " + this); + sendAddVideoContent(); + return true; + } + return false; + } + } + else + { + if (direction == MediaDirection.INACTIVE) + { + sendRemoveVideoContent(); + return true; + } + } + + SendersEnum senders = getSenders(MediaType.VIDEO); + if (senders == null) + senders = SendersEnum.both; + + SendersEnum newSenders = SendersEnum.none; + if (MediaDirection.SENDRECV == direction) + newSenders = SendersEnum.both; + else if (MediaDirection.RECVONLY == direction) + newSenders = isInitiator() + ? SendersEnum.initiator : SendersEnum.responder; + else if (MediaDirection.SENDONLY == direction) + newSenders = isInitiator() + ? SendersEnum.responder : SendersEnum.initiator; + + /* + * Send Content-Modify + */ + ContentPacketExtension ext = new ContentPacketExtension(); + String remoteContentName = remoteContent.getName(); + + ext.setSenders(newSenders); + ext.setCreator(remoteContent.getCreator()); + ext.setName(remoteContentName); + + if (newSenders != senders) + { + if (logger.isInfoEnabled()) + logger.info("Sending content modify, senders: " + + senders + "->" + newSenders); + ProtocolProviderServiceJabberImpl protocolProvider + = getProtocolProvider(); + JingleIQ contentIQ + = JinglePacketFactory.createContentModify( + protocolProvider.getOurJID(), + this.peerJID, + getSID(), + ext); + + protocolProvider.getConnection().sendPacket(contentIQ); + } + + try + { + mediaHandler.reinitContent(remoteContentName, ext, false); + mediaHandler.start(); + } + catch(Exception e) + { + logger.warn("Exception occurred during media reinitialization", e); + } + + return (newSenders != senders); + } + + /** + * Send a <tt>content</tt> message to reflect change in video setup (start + * or stop). + */ + public void sendModifyVideoResolutionContent() + { + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + ContentPacketExtension remoteContent + = mediaHandler.getRemoteContent(MediaType.VIDEO.toString()); + ContentPacketExtension content; + + logger.info("send modify-content to change resolution"); + + // send content-modify with RTP description + + // create content list with resolution + try + { + content = mediaHandler.createContentForMedia(MediaType.VIDEO); + } + catch (Exception e) + { + logger.warn("Failed to gather content for video type", e); + return; + } + + // if we are only receiving video senders is null + SendersEnum senders = remoteContent.getSenders(); + + if (senders != null) + content.setSenders(senders); + + ProtocolProviderServiceJabberImpl protocolProvider + = getProtocolProvider(); + JingleIQ contentIQ + = JinglePacketFactory.createContentModify( + protocolProvider.getOurJID(), + this.peerJID, + getSID(), + content); + + protocolProvider.getConnection().sendPacket(contentIQ); + + try + { + mediaHandler.reinitContent(remoteContent.getName(), content, false); + mediaHandler.start(); + } + catch(Exception e) + { + logger.warn("Exception occurred when media reinitialization", e); + } + } + + /** + * Send a <tt>content-remove</tt> to remove video setup. + */ + private void sendRemoveVideoContent() + { + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + + ContentPacketExtension content = new ContentPacketExtension(); + ContentPacketExtension remoteContent + = mediaHandler.getRemoteContent(MediaType.VIDEO.toString()); + if (remoteContent == null) + return; + String remoteContentName = remoteContent.getName(); + + content.setName(remoteContentName); + content.setCreator(remoteContent.getCreator()); + content.setSenders(remoteContent.getSenders()); + + ProtocolProviderServiceJabberImpl protocolProvider + = getProtocolProvider(); + JingleIQ contentIQ + = JinglePacketFactory.createContentRemove( + protocolProvider.getOurJID(), + this.peerJID, + getSID(), + Arrays.asList(content)); + + protocolProvider.getConnection().sendPacket(contentIQ); + mediaHandler.removeContent(remoteContentName); + setSenders(MediaType.VIDEO, SendersEnum.none); + } + + /** + * Sends local candidate addresses from the local peer to the remote peer + * using the <tt>transport-info</tt> {@link JingleIQ}. + * + * @param contents the local candidate addresses to be sent from the local + * peer to the remote peer using the <tt>transport-info</tt> + * {@link JingleIQ} + */ + protected void sendTransportInfo(Iterable<ContentPacketExtension> contents) + { + // if the call is canceled, do not start sending candidates in + // transport-info + if(cancelled) + return; + + JingleIQ transportInfo = new JingleIQ(); + + for (ContentPacketExtension content : contents) + transportInfo.addContent(content); + + ProtocolProviderServiceJabberImpl protocolProvider + = getProtocolProvider(); + + transportInfo.setAction(JingleAction.TRANSPORT_INFO); + transportInfo.setFrom(protocolProvider.getOurJID()); + transportInfo.setSID(getSID()); + transportInfo.setTo(getAddress()); + transportInfo.setType(IQ.Type.SET); + + PacketCollector collector + = protocolProvider.getConnection().createPacketCollector( + new PacketIDFilter(transportInfo.getPacketID())); + + protocolProvider.getConnection().sendPacket(transportInfo); + collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); + collector.cancel(); + } + + @Override + public void setState(CallPeerState newState, String reason, int reasonCode) + { + CallPeerState oldState = getState(); + try + { + /* + * We need to dispose of the transport manager before the + * 'call' field is set to null, because if Jitsi Videobridge is in + * use, it (the call) is needed in order to expire the + * Videobridge channels. + */ + if (CallPeerState.DISCONNECTED.equals(newState) + || CallPeerState.FAILED.equals(newState)) + getMediaHandler().getTransportManager().close(); + } + finally + { + super.setState(newState, reason, reasonCode); + } + + if (CallPeerState.isOnHold(oldState) + && CallPeerState.CONNECTED.equals(newState)) + { + try + { + getCall().modifyVideoContent(); + } + catch (OperationFailedException ofe) + { + logger.error("Failed to update call video state after " + + "'hold' status removed for "+this); + } + } + } + + /** + * Transfer (in the sense of call transfer) this <tt>CallPeer</tt> to a + * specific callee address which may optionally be participating in an + * active <tt>Call</tt>. + * + * @param to the address of the callee to transfer this <tt>CallPeer</tt> to + * @param sid the Jingle session ID of the active <tt>Call</tt> between the + * local peer and the callee in the case of attended transfer; <tt>null</tt> + * in the case of unattended transfer + * @throws OperationFailedException if something goes wrong + */ + protected void transfer(String to, String sid) + throws OperationFailedException + { + JingleIQ transferSessionInfo = new JingleIQ(); + ProtocolProviderServiceJabberImpl protocolProvider + = getProtocolProvider(); + + transferSessionInfo.setAction(JingleAction.SESSION_INFO); + transferSessionInfo.setFrom(protocolProvider.getOurJID()); + transferSessionInfo.setSID(getSID()); + transferSessionInfo.setTo(getAddress()); + transferSessionInfo.setType(IQ.Type.SET); + + TransferPacketExtension transfer = new TransferPacketExtension(); + + // Attended transfer. + if (sid != null) + { + /* + * Not really sure what the value of the "from" attribute of the + * "transfer" element should be but the examples in "XEP-0251: + * Jingle Session Transfer" has it in the case of attended transfer. + */ + transfer.setFrom(protocolProvider.getOurJID()); + transfer.setSID(sid); + + // Puts on hold the 2 calls before making the attended transfer. + OperationSetBasicTelephonyJabberImpl basicTelephony + = (OperationSetBasicTelephonyJabberImpl) + protocolProvider.getOperationSet( + OperationSetBasicTelephony.class); + CallPeerJabberImpl callPeer = basicTelephony.getActiveCallPeer(sid); + if(callPeer != null) + { + if(!CallPeerState.isOnHold(callPeer.getState())) + { + callPeer.putOnHold(true); + } + } + + if(!CallPeerState.isOnHold(this.getState())) + { + this.putOnHold(true); + } + } + transfer.setTo(to); + + transferSessionInfo.addExtension(transfer); + + Connection connection = protocolProvider.getConnection(); + PacketCollector collector = connection.createPacketCollector( + new PacketIDFilter(transferSessionInfo.getPacketID())); + protocolProvider.getConnection().sendPacket(transferSessionInfo); + + Packet result + = collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); + + if(result == null) + { + // Log the failed transfer call and notify the user. + throw new OperationFailedException( + "No response to the \"transfer\" request.", + OperationFailedException.ILLEGAL_ARGUMENT); + } + else if (((IQ) result).getType() != IQ.Type.RESULT) + { + // Log the failed transfer call and notify the user. + throw new OperationFailedException( + "Remote peer does not manage call \"transfer\"." + + "Response to the \"transfer\" request is: " + + ((IQ) result).getType(), + OperationFailedException.ILLEGAL_ARGUMENT); + } + else + { + String message = ((sid == null) ? "Unattended" : "Attended") + + " transfer to: " + + to; + // Implements the SIP behavior: once the transfer is accepted, the + // current call is closed. + hangup( + false, + message, + new ReasonPacketExtension(Reason.SUCCESS, + message, + new TransferredPacketExtension())); + } + } + + /** + * {@inheritDoc} + */ + public String getEntity() + { + return getAddress(); + } + + /** + * {@inheritDoc} + * + * In Jingle there isn't an actual "direction" parameter. We use the + * <tt>senders</tt> field to calculate the direction. + */ + @Override + public MediaDirection getDirection(MediaType mediaType) + { + SendersEnum senders = getSenders(mediaType); + + if (senders == SendersEnum.none) + { + return MediaDirection.INACTIVE; + } + else if (senders == null || senders == SendersEnum.both) + { + return MediaDirection.SENDRECV; + } + else if (senders == SendersEnum.initiator) + { + return + isInitiator() + ? MediaDirection.RECVONLY + : MediaDirection.SENDONLY; + } + else //senders == SendersEnum.responder + { + return + isInitiator() + ? MediaDirection.SENDONLY + : MediaDirection.RECVONLY; + } + } + + /** + * Gets the current value of the <tt>senders</tt> field of the content with + * name <tt>mediaType</tt> in the Jingle session with this + * <tt>CallPeer</tt>. + * + * @param mediaType the <tt>MediaType</tt> for which to get the current + * value of the <tt>senders</tt> field. + * @return the current value of the <tt>senders</tt> field of the content + * with name <tt>mediaType</tt> in the Jingle session with this + * <tt>CallPeer</tt>. + */ + public SendersEnum getSenders(MediaType mediaType) + { + switch (mediaType) + { + case AUDIO: + return audioSenders; + case VIDEO: + return videoSenders; + default: + return SendersEnum.none; + } + } + + /** + * Set the current value of the <tt>senders</tt> field of the content with + * name <tt>mediaType</tt> in the Jingle session with this <tt>CallPeer</tt> + * @param mediaType the <tt>MediaType</tt> for which to get the current + * value of the <tt>senders</tt> field. + * @param senders the value to set + */ + public void setSenders(MediaType mediaType, SendersEnum senders) + { + switch(mediaType) + { + case AUDIO: + this.audioSenders = senders; + break; + case VIDEO: + this.videoSenders = senders; + break; + default: + throw new IllegalArgumentException("mediaType"); + } + } + + /** + * Gets the <tt>MediaType</tt> of <tt>content</tt>. If <tt>content</tt> + * does not have a <tt>description</tt> child and therefore not + * <tt>MediaType</tt> can be associated with it, tries to take the + * <tt>MediaType</tt> from the session's already established contents with + * the same name as <tt>content</tt> + * @param content the <tt>ContentPacketExtention</tt> for which to get the + * <tt>MediaType</tt> + * @return the <tt>MediaType</tt> of <tt>content</tt>. + */ + public MediaType getMediaType(ContentPacketExtension content) + { + String contentName = content.getName(); + if (contentName == null) + return null; + + MediaType mediaType = JingleUtils.getMediaType(content); + if (mediaType == null) + { + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + for (MediaType m : MediaType.values()) + { + ContentPacketExtension sessionContent + = mediaHandler.getRemoteContent(m.toString()); + if (sessionContent == null) + sessionContent = mediaHandler.getLocalContent(m.toString()); + + if (sessionContent != null + && contentName.equals(sessionContent.getName())) + { + mediaType = m; + break; + } + } + } + + return mediaType; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java index 2c8845d..979d696 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java @@ -937,19 +937,18 @@ public class CallPeerMediaHandlerJabberImpl if (supportedTransports != null && supportedTransports.length > 0) { - for (int i = 0; i < supportedTransports.length; i++) + for(String supportedTransport : supportedTransports) { if (ProtocolProviderServiceJabberImpl. URN_XMPP_JINGLE_ICE_UDP_1. - equals(supportedTransports[i])) + equals(supportedTransport)) { - transportManager - = new IceUdpTransportManager(peer); + transportManager = new IceUdpTransportManager(peer); break; } else if (ProtocolProviderServiceJabberImpl. - URN_XMPP_JINGLE_RAW_UDP_0. - equals(supportedTransports[i])) + URN_XMPP_JINGLE_RAW_UDP_0. + equals(supportedTransport)) { transportManager = new RawUdpTransportManager(peer); @@ -1117,12 +1116,11 @@ public class CallPeerMediaHandlerJabberImpl List<Component> visualComponents = new LinkedList<Component>(); - for (int i = 0; i < remoteSSRCs.length; i++) + for(int remoteSSRC : remoteSSRCs) { - int remoteSSRC = remoteSSRCs[i]; Component visualComponent - = videoStream.getVisualComponent( - 0xFFFFFFFFL & remoteSSRC); + = videoStream.getVisualComponent( + 0xFFFFFFFFL & remoteSSRC); if (visualComponent != null) visualComponents.add(visualComponent); @@ -1605,7 +1603,7 @@ public class CallPeerMediaHandlerJabberImpl { List<MediaFormat> fmts = supportedFormats; - if(fmts.size() > 0) + if(!fmts.isEmpty()) { MediaFormat fmt = fmts.get(0); @@ -2108,21 +2106,17 @@ public class CallPeerMediaHandlerJabberImpl * TODO The transportManager is going to be changed so it may need to be * disposed of prior to the change. */ - - if (xmlns.equals( - ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_ICE_UDP_1)) + switch (xmlns) { - transportManager = new IceUdpTransportManager(peer); - } - else if (xmlns.equals( - ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0)) - { - transportManager = new RawUdpTransportManager(peer); - } - else - { - throw new IllegalArgumentException( - "Unsupported Jingle transport " + xmlns); + case ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_ICE_UDP_1: + transportManager = new IceUdpTransportManager(peer); + break; + case ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0: + transportManager = new RawUdpTransportManager(peer); + break; + default: + throw new IllegalArgumentException("Unsupported Jingle " + + "transport " + xmlns); } synchronized(transportManagerSyncRoot) diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ChatRoomJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ChatRoomJabberImpl.java index 8a6f3ed..6c4c358 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ChatRoomJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ChatRoomJabberImpl.java @@ -609,7 +609,7 @@ public class ChatRoomJabberImpl this.provider.getConnection().addPacketListener( presenceListener, new AndFilter( - new FromMatchesFilter(multiUserChat.getRoom()), + FromMatchesFilter.create(multiUserChat.getRoom()), new PacketTypeFilter( org.jivesoftware.smack.packet.Presence.class))); if(password == null) @@ -868,7 +868,7 @@ public class ChatRoomJabberImpl clearCachedConferenceDescriptionList(); - XMPPConnection connection = this.provider.getConnection(); + Connection connection = this.provider.getConnection(); try { // if we are already disconnected @@ -1896,6 +1896,23 @@ public class ChatRoomJabberImpl } /** + * Removes given <tt>PacketExtension</tt> from the MUC presence and + * publishes it immediately. + * @param extension the <tt>PacketExtension</tt> to be removed from the MUC + * presence. + */ + public void removePresenceExtension(PacketExtension extension) + { + if (lastPresenceSent != null) + { + setPacketExtension( + lastPresenceSent, null, extension.getNamespace()); + + provider.getConnection().sendPacket(lastPresenceSent); + } + } + + /** * Returns the ids of the users that has the member role in the room. * When the room is member only, this are the users allowed to join. * @return the ids of the users that has the member role in the room. diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java index 1f4e3d8..c3c852c 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java @@ -58,11 +58,21 @@ public class IceUdpTransportManager = Logger.getLogger(IceUdpTransportManager.class); /** + * Default STUN server address. + */ + protected static final String DEFAULT_STUN_SERVER_ADDRESS = "stun.jitsi.net"; + + /** + * Default STUN server port. + */ + protected static final int DEFAULT_STUN_SERVER_PORT = 3478; + + /** * The ICE <tt>Component</tt> IDs in their common order used, for example, * by <tt>DefaultStreamConnector</tt>, <tt>MediaStreamTarget</tt>. */ private static final int[] COMPONENT_IDS - = new int[] { Component.RTP, Component.RTCP }; + = new int[] { Component.RTP, Component.RTCP }; /** * This is where we keep our answer between the time we get the offer and @@ -76,15 +86,6 @@ public class IceUdpTransportManager */ protected final Agent iceAgent; - /** - * Default STUN server address. - */ - protected static final String DEFAULT_STUN_SERVER_ADDRESS = "stun.jitsi.net"; - - /** - * Default STUN server port. - */ - protected static final int DEFAULT_STUN_SERVER_PORT = 3478; /** * Creates a new instance of this transport manager, binding it to the @@ -156,7 +157,10 @@ public class IceUdpTransportManager // in case user has canceled the login window if(credentials == null) + { + logger.info("Credentials were null. User has most likely canceled the login operation"); return null; + } //extract the password the user passed us. char[] pass = credentials.getPassword(); @@ -164,7 +168,10 @@ public class IceUdpTransportManager // the user didn't provide us a password (i.e. canceled the // operation) if(pass == null) + { + logger.info("Password was null. User has most likely canceled the login operation"); return null; + } password = new String(pass); if (credentials.isPasswordPersistent()) @@ -390,28 +397,29 @@ public class IceUdpTransportManager for (int i = 0; i < COMPONENT_IDS.length; i++) { Component component = stream.getComponent(COMPONENT_IDS[i]); - - if (component != null) + if (component == null) { - CandidatePair selectedPair = component.getSelectedPair(); - - if (selectedPair != null) - { - DatagramSocket streamConnectorSocket - = selectedPair.getLocalCandidate(). - getDatagramSocket(); + continue; + } - if (streamConnectorSocket != null) - { - streamConnectorSockets[i] = streamConnectorSocket; - streamConnectorSocketCount++; - } - } + DatagramSocket streamConnectorSocket = component.getSocket(); + if (streamConnectorSocket != null) + { + streamConnectorSockets[i] = streamConnectorSocket; + streamConnectorSocketCount++; + logger.trace("Added a streamConnectorSocket to the array " + + "StreamConnectorSocket and increased " + + "the count of streamConnectorSocketCount by one to " + + streamConnectorSocketCount); } } + if (streamConnectorSocketCount > 0) + { return streamConnectorSockets; + } } + return null; } @@ -742,20 +750,25 @@ public class IceUdpTransportManager ex); } - //let's now update the next port var as best we can: we would assume - //that all local candidates are bound on the same port and set it - //to the one just above. if the assumption is wrong the next bind - //would simply include one more bind retry. + // Attempt to minimize subsequent bind retries: see if we have allocated + // any ports from the dynamic range, and if so update the port tracker. + // Do NOT update the port tracker with non-dynamic ports (e.g. 4443 + // coming from TCP) because this will force it to revert back it its + // configured min port. When maxPort is reached, allocation will begin + // from minPort again, so we don't have to worry about wraps. try { - portTracker.setNextPort( - 1 - + stream - .getComponent(Component.RTCP) - .getLocalCandidates() - .get(0) - .getTransportAddress() - .getPort()); + int maxAllocatedPort = getMaxAllocatedPort( + stream, + portTracker.getMinPort(), + portTracker.getMaxPort()); + + if(maxAllocatedPort > 0) + { + int nextPort = 1 + maxAllocatedPort; + portTracker.setNextPort(nextPort); + logger.debug("Updating the port tracker min port: " + nextPort); + } } catch(Throwable t) { @@ -768,6 +781,48 @@ public class IceUdpTransportManager } /** + * @return the highest local port used by any of the local candidates of + * {@code iceStream}, which falls in the range [{@code min}, {@code max}]. + */ + private int getMaxAllocatedPort(IceMediaStream iceStream, int min, int max) + { + return + Math.max( + getMaxAllocatedPort( + iceStream.getComponent(Component.RTP), + min, max), + getMaxAllocatedPort( + iceStream.getComponent(Component.RTCP), + min, max)); + } + + /** + * @return the highest local port used by any of the local candidates of + * {@code component}, which falls in the range [{@code min}, {@code max}]. + */ + private int getMaxAllocatedPort(Component component, int min, int max) + { + int maxAllocatedPort = -1; + + if (component != null) + { + for (LocalCandidate candidate : component.getLocalCandidates()) + { + int candidatePort = candidate.getTransportAddress().getPort(); + + if (min <= candidatePort + && candidatePort <= max + && maxAllocatedPort < candidatePort) + { + maxAllocatedPort = candidatePort; + } + } + } + + return maxAllocatedPort; + } + + /** * Simply returns the list of local candidates that we gathered during the * harvest. * @@ -898,8 +953,12 @@ public class IceUdpTransportManager = transport.getChildExtensionsOfType( CandidatePacketExtension.class); - if (iceAgentStateIsRunning && (candidates.size() == 0)) + if (iceAgentStateIsRunning && candidates.isEmpty()) + { + logger.info("connectivity establishment has not been started " + + "because candidate list is empty"); return false; + } String media = e.getKey(); IceMediaStream stream = iceAgent.getStream(media); @@ -938,6 +997,12 @@ public class IceUdpTransportManager if (candidate.getGeneration() != generation) continue; + if (candidate.getIP() == null || "".equals(candidate.getIP())) + { + logger.warn("Skipped ICE candidate with empty IP"); + continue; + } + Component component = stream.getComponent(candidate.getComponent()); String relAddr; diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java b/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java index a0387df..c3137ad 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java @@ -151,7 +151,7 @@ public class InfoRetreiver List<GenericDetail> result = new LinkedList<GenericDetail>(); try { - XMPPConnection connection = jabberProvider.getConnection(); + Connection connection = jabberProvider.getConnection(); if(connection == null || !connection.isAuthenticated()) return null; diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JabberLoginStrategy.java b/src/net/java/sip/communicator/impl/protocol/jabber/JabberLoginStrategy.java index 438ea1b..de7578c 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberLoginStrategy.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberLoginStrategy.java @@ -61,7 +61,7 @@ public interface JabberLoginStrategy * @param resource the XMPP resource * @return true to continue connecting, false to abort */ - public boolean login(XMPPConnection connection, String userName, + public boolean login(Connection connection, String userName, String resource) throws XMPPException; diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java index 0ea9a54..f2473dd 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java @@ -87,7 +87,7 @@ public class JingleNodesCandidate * @return the <tt>RelayedCandidateDatagramSocket</tt> of this * <tt>RelayedCandidate</tt> */ - public synchronized JingleNodesCandidateDatagramSocket + private synchronized JingleNodesCandidateDatagramSocket getRelayedCandidateDatagramSocket() { if (jingleNodesCandidateDatagramSocket == null) @@ -113,7 +113,7 @@ public class JingleNodesCandidate * <tt>Candidate</tt> */ @Override - public IceSocketWrapper getIceSocketWrapper() + protected IceSocketWrapper getCandidateIceSocketWrapper() { if (socket == null) { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java index bb7d31b..d29be03 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java @@ -36,7 +36,7 @@ import org.xmpp.jnodes.smack.*; * @author Sebastien Vincent */ public class JingleNodesHarvester - extends CandidateHarvester + extends AbstractCandidateHarvester { /** * The <tt>Logger</tt> used by the <tt>JingleNodesHarvester</tt> class and @@ -125,7 +125,7 @@ public class JingleNodesHarvester } } - if (ciq != null && ciq.getRemoteport() > 0) + if (ciq != null) { ip = ciq.getHost(); port = ciq.getRemoteport(); @@ -136,6 +136,22 @@ public class JingleNodesHarvester " local port: " + ciq.getLocalport()); } + if (ip == null || ciq.getRemoteport() == 0) + { + logger.warn("JN relay ignored because ip was null or port 0"); + return candidates; + } + + // Drop the scope or interface name if the relay sends it + // along in its IPv6 address. The scope/ifname is only valid on the + // host that owns the IP and we don't need it here. + int scopeIndex = ip.indexOf('%'); + if (scopeIndex > 0) + { + logger.warn("Dropping scope from assumed IPv6 address " + ip); + ip = ip.substring(0, scopeIndex); + } + /* RTP */ TransportAddress relayedAddress = new TransportAddress(ip, port, Transport.UDP); @@ -160,6 +176,7 @@ public class JingleNodesHarvester candidates.add(local); } } + return candidates; } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/LoginByClientCertificateStrategy.java b/src/net/java/sip/communicator/impl/protocol/jabber/LoginByClientCertificateStrategy.java index 09c9462..4825e01 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/LoginByClientCertificateStrategy.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/LoginByClientCertificateStrategy.java @@ -116,7 +116,7 @@ class LoginByClientCertificateStrategy * accepted. * @throws XMPPException */ - public boolean login(XMPPConnection connection, String userName, + public boolean login(Connection connection, String userName, String resource) throws XMPPException { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/LoginByPasswordStrategy.java b/src/net/java/sip/communicator/impl/protocol/jabber/LoginByPasswordStrategy.java index 43fc8a4..7034221 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/LoginByPasswordStrategy.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/LoginByPasswordStrategy.java @@ -115,7 +115,7 @@ public class LoginByPasswordStrategy * @return always true. * @throws XMPPException */ - public boolean login(XMPPConnection connection, String userName, + public boolean login(Connection connection, String userName, String resource) throws XMPPException { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/MobileIndicator.java b/src/net/java/sip/communicator/impl/protocol/jabber/MobileIndicator.java index 926848c..8c801c6 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/MobileIndicator.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/MobileIndicator.java @@ -222,11 +222,13 @@ public class MobileIndicator /** * Caps for user has been changed. * @param user the user (full JID) + * @param fullJids a list of all resources of the user (full JIDs) * @param node the entity caps node#ver * @param online indicates if the user for which we're notified is online */ @Override - public void userCapsNodeAdded(String user, String node, boolean online) + public void userCapsNodeAdded(String user, ArrayList<String> fullJids, + String node, boolean online) { updateMobileIndicatorUsingCaps(user); } @@ -234,11 +236,13 @@ public class MobileIndicator /** * Caps for user has been changed. * @param user the user (full JID) + * @param fullJids a list of all resources of the user (full JIDs) * @param node the entity caps node#ver * @param online indicates if the user for which we're notified is online */ @Override - public void userCapsNodeRemoved(String user, String node, boolean online) + public void userCapsNodeRemoved(String user, ArrayList<String> fullJids, + String node, boolean online) { updateMobileIndicatorUsingCaps(user); } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java index 42a3916..2f35fef 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java @@ -815,6 +815,17 @@ public class OperationSetBasicInstantMessagingJabberImpl ForwardedPacketExtension.class); if(extensions.isEmpty()) return; + + // according to xep-0280 all carbons should come from + // our bare jid + if (!msg.getFrom().equals( + StringUtils.parseBareAddress( + jabberProvider.getOurJID()))) + { + logger.info("Received a carbon copy with wrong from!"); + return; + } + ForwardedPacketExtension forwardedExt = extensions.get(0); msg = forwardedExt.getMessage(); if(msg == null || msg.getBody() == null) @@ -1109,7 +1120,7 @@ public class OperationSetBasicInstantMessagingJabberImpl NewMailNotificationIQ.NAMESPACE, new NewMailNotificationProvider()); - XMPPConnection connection = jabberProvider.getConnection(); + Connection connection = jabberProvider.getConnection(); connection.addPacketListener( new MailboxIQListener(), new PacketTypeFilter(MailboxIQ.class)); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java index cc36936..78027c0 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java @@ -280,7 +280,10 @@ public class OperationSetBasicTelephonyJabberImpl Iterable<PacketExtension> sessionInitiateExtensions) throws OperationFailedException { - return createOutgoingCall(call, calleeAddress, null, null); + if (calleeAddress.contains("/")) + return createOutgoingCall(call, calleeAddress, calleeAddress, null); + else + return createOutgoingCall(call, calleeAddress, null, null); } /** @@ -774,7 +777,7 @@ public class OperationSetBasicTelephonyJabberImpl */ private void unsubscribeForJinglePackets() { - XMPPConnection connection = protocolProvider.getConnection(); + Connection connection = protocolProvider.getConnection(); if(connection != null) connection.removePacketListener(this); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java index 0a6edfe..59cc17c 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java @@ -26,7 +26,7 @@ import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; import org.jivesoftware.smack.packet.*; -import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.*; /** * Represents an <tt>OperationSet</tt> to query the <tt>OperationSet</tt>s @@ -52,6 +52,19 @@ public class OperationSetContactCapabilitiesJabberImpl = Logger.getLogger(OperationSetContactCapabilitiesJabberImpl.class); /** + * The name of the property used to control whether to use + * all resources to show capabilities + */ + public static final String PROP_XMPP_USE_ALL_RESOURCES_FOR_CAPABILITIES = + "net.java.sip.communicator.XMPP_USE_ALL_RESOURCES_FOR_CAPABILITIES"; + + /** + * The default value for the capabilities setting + */ + public static final boolean USE_ALL_RESOURCES_FOR_CAPABILITIES_DEFAULT = + true; + + /** * The list of <tt>OperationSet</tt> capabilities presumed to be supported * by a <tt>Contact</tt> when it is offline. */ @@ -276,6 +289,42 @@ public class OperationSetContactCapabilitiesJabberImpl } /** + * Gets the largest set of <tt>OperationSet</tt>s supported from a + * list of full JIDs. The returned <tt>OperationSet</tt>s are considered + * by the associated protocol provider to capabilities possessed by the + * specified <tt>contact</tt>. + * + * @param fullJids a list of full JIDs in which to find the resource with + * the most capabilities. + * @return the <tt>Map</tt> listing the most <tt>OperationSet</tt>s + * considered by the associated protocol provider to be supported by the + * specified <tt>contact</tt> (i.e. to be possessed as capabilities). + * Each supported <tt>OperationSet</tt> capability is represented by a + * <tt>Map.Entry</tt> with key equal to the <tt>OperationSet</tt> class + * name and value equal to the respective <tt>OperationSet</tt> instance + */ + protected Map<String, OperationSet> getLargestSupportedOperationSet( + ArrayList<String> fullJids) + { + Map<String, OperationSet> supportedOperationSets = + new HashMap<String, OperationSet>(); + if (fullJids!=null) + { + for (String fullJid : fullJids) + { + Map<String, OperationSet> newSupportedOperationSets= + getSupportedOperationSets(fullJid, true); + if (newSupportedOperationSets.size()> + supportedOperationSets.size()) + { + supportedOperationSets = newSupportedOperationSets; + } + } + } + return supportedOperationSets; + } + + /** * Gets the <tt>OperationSet</tt> corresponding to the specified * <tt>Class</tt> and supported by the specified <tt>Contact</tt>. If the * returned value is non-<tt>null</tt>, it indicates that the @@ -387,17 +436,19 @@ public class OperationSetContactCapabilitiesJabberImpl * record for a specific user about the caps node the user has. * * @param user the user (full JID) + * @param fullJids a list of all resources of the user (full JIDs) * @param node the entity caps node#ver * @param online indicates if the user is currently online * @see UserCapsNodeListener#userCapsNodeAdded(String, String, boolean) */ - public void userCapsNodeAdded(String user, String node, boolean online) + public void userCapsNodeAdded(String user, ArrayList<String> fullJids, + String node, boolean online) { /* * It doesn't matter to us whether a caps node has been added or removed * for the specified user because we report all changes. */ - userCapsNodeRemoved(user, node, online); + userCapsNodeChanged(user, fullJids, node, online); } /** @@ -405,45 +456,86 @@ public class OperationSetContactCapabilitiesJabberImpl * record for a specific user about the caps node the user has. * * @param user the user (full JID) + * @param fullJids a list of all resources of the user (full JIDs) + * @param node the entity caps node#ver + * @param online indicates if the user is currently online + * @see UserCapsNodeListener#userCapsNodeAdded(String, String, boolean) + */ + public void userCapsNodeRemoved(String user, ArrayList<String> fullJids, + String node, boolean online) + { + /* + * It doesn't matter to us whether a caps node has been added or removed + * for the specified user because we report all changes. + */ + userCapsNodeChanged(user, fullJids, node, online); + } + + /** + * Notifies this listener that an <tt>EntityCapsManager</tt> has changed a + * record for a specific user about the caps node the user has. + * + * @param user the user (full JID) + * @param fullJids a list of all resources of the user (full JIDs) * @param node the entity caps node#ver * @param online indicates if the given user is online - * @see UserCapsNodeListener#userCapsNodeRemoved(String, String, boolean) */ - public void userCapsNodeRemoved(String user, String node, boolean online) + public void userCapsNodeChanged(String user, ArrayList<String> fullJids, + String node, boolean online) { OperationSetPresence opsetPresence - = parentProvider.getOperationSet(OperationSetPresence.class); - - if (opsetPresence != null) - { - String jid = StringUtils.parseBareAddress(user); - Contact contact = opsetPresence.findContactByID(jid); - - // If the contact isn't null and is online we try to discover the - // new set of operation sets and to notify interested parties. - // Otherwise we ignore the event. - if (contact != null) + = parentProvider.getOperationSet(OperationSetPresence.class); + if (opsetPresence != null) { + if(JabberActivator.getConfigurationService() + .getBoolean( + PROP_XMPP_USE_ALL_RESOURCES_FOR_CAPABILITIES, + USE_ALL_RESOURCES_FOR_CAPABILITIES_DEFAULT) + && !fullJids.isEmpty()) { - if(online) + String bareJid = StringUtils.parseBareAddress(user); + Contact contact = opsetPresence.findContactByID(bareJid); + if (contact != null) { - // when going online we have received a presence - // and make sure we discover this particular jid - // for getSupportedOperationSets fireContactCapabilitiesEvent( contact, - ContactCapabilitiesEvent.SUPPORTED_OPERATION_SETS_CHANGED, - getSupportedOperationSets(user, - online)); + ContactCapabilitiesEvent. + SUPPORTED_OPERATION_SETS_CHANGED, + getLargestSupportedOperationSet(fullJids)); } - else + } + else + { + String jid = StringUtils.parseBareAddress(user); + Contact contact = opsetPresence.findContactByID(jid); + + // If the contact isn't null and is online we try to discover + // the new set of operation sets and to notify interested + // parties. Otherwise we ignore the event. + if (contact != null) { - // when offline, we use the contact, and selecting - // the most connected jid - // for getSupportedOperationSets - fireContactCapabilitiesEvent( - contact, - ContactCapabilitiesEvent.SUPPORTED_OPERATION_SETS_CHANGED, - getSupportedOperationSets(contact)); + if(online) + { + // when going online we have received a presence + // and make sure we discover this particular jid + // for getSupportedOperationSets + fireContactCapabilitiesEvent( + contact, + ContactCapabilitiesEvent. + SUPPORTED_OPERATION_SETS_CHANGED, + getSupportedOperationSets(user, + online)); + } + else + { + // when offline, we use the contact, and selecting + // the most connected jid + // for getSupportedOperationSets + fireContactCapabilitiesEvent( + contact, + ContactCapabilitiesEvent. + SUPPORTED_OPERATION_SETS_CHANGED, + getSupportedOperationSets(contact)); + } } } } @@ -460,7 +552,8 @@ public class OperationSetContactCapabilitiesJabberImpl { // If the user goes offline we ensure to remove the caps node. if (capsManager != null - && evt.getNewStatus().getStatus() < PresenceStatus.ONLINE_THRESHOLD) + && evt.getNewStatus().getStatus() < PresenceStatus.ONLINE_THRESHOLD + && !evt.isResourceChanged()) { capsManager.removeContactCapsNode(evt.getSourceContact()); } @@ -469,31 +562,59 @@ public class OperationSetContactCapabilitiesJabberImpl /** * Fires event that contact capabilities has changed. * @param user the user to search for its contact. + * @param fullJids a list of all resources of the user (full JIDs) */ - public void fireContactCapabilitiesChanged(String user) + public void fireContactCapabilitiesChanged(String user, + ArrayList<String> fullJids) { - OperationSetPresence opsetPresence + if(!JabberActivator.getConfigurationService() + .getBoolean( + PROP_XMPP_USE_ALL_RESOURCES_FOR_CAPABILITIES, + USE_ALL_RESOURCES_FOR_CAPABILITIES_DEFAULT) + || fullJids.isEmpty()) + { + OperationSetPresence opsetPresence = parentProvider.getOperationSet(OperationSetPresence.class); - if (opsetPresence != null) + if (opsetPresence != null) + { + String userID = StringUtils.parseBareAddress(user); + Contact contact = opsetPresence.findContactByID(userID); + + // this called by received discovery info for particular jid + // so we use its online and opsets for this particular jid + boolean online = false; + Presence presence = parentProvider.getConnection().getRoster() + .getPresence(user); + if(presence != null) + online = presence.isAvailable(); + + if(contact != null) + { + fireContactCapabilitiesEvent( + contact, + ContactCapabilitiesEvent. + SUPPORTED_OPERATION_SETS_CHANGED, + getSupportedOperationSets(user, online)); + } + } + } + else { - String userID = StringUtils.parseBareAddress(user); - Contact contact = opsetPresence.findContactByID(userID); - - // this called by received discovery info for particular jid - // so we use its online and opsets for this particular jid - boolean online = false; - Presence presence = parentProvider.getConnection().getRoster() - .getPresence(user); - if(presence != null) - online = presence.isAvailable(); - - if(contact != null) + OperationSetPresence opsetPresence + = parentProvider.getOperationSet(OperationSetPresence.class); + if (opsetPresence != null) { - fireContactCapabilitiesEvent( - contact, - ContactCapabilitiesEvent.SUPPORTED_OPERATION_SETS_CHANGED, - getSupportedOperationSets(user, online)); + String bareJid = StringUtils.parseBareAddress(user); + Contact contact = opsetPresence.findContactByID(bareJid); + if(contact != null) + { + fireContactCapabilitiesEvent( + contact, + ContactCapabilitiesEvent. + SUPPORTED_OPERATION_SETS_CHANGED, + getLargestSupportedOperationSet(fullJids)); + } } } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetJitsiMeetToolsJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetJitsiMeetToolsJabberImpl.java index 0fb6979..7ea4453 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetJitsiMeetToolsJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetJitsiMeetToolsJabberImpl.java @@ -72,6 +72,16 @@ public class OperationSetJitsiMeetToolsJabberImpl * {@inheritDoc} */ @Override + public void removePresenceExtension(ChatRoom chatRoom, + PacketExtension extension) + { + ((ChatRoomJabberImpl)chatRoom).removePresenceExtension(extension); + } + + /** + * {@inheritDoc} + */ + @Override public void setPresenceStatus(ChatRoom chatRoom, String statusMessage) { ((ChatRoomJabberImpl)chatRoom).publishPresenceStatus(statusMessage); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetMultiUserChatJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetMultiUserChatJabberImpl.java index 80bbb4e..cf53906 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetMultiUserChatJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetMultiUserChatJabberImpl.java @@ -471,10 +471,10 @@ public class OperationSetMultiUserChatJabberImpl * Almost all <tt>MultiUserChat</tt> methods require an xmpp connection * param so I added this method only for the sake of utility. * - * @return the XMPPConnection currently in use by the jabber provider or + * @return the XMPP connection currently in use by the jabber provider or * null if jabber provider has yet to be initialized. */ - private XMPPConnection getXmppConnection() + private Connection getXmppConnection() { return (jabberProvider == null) ? null diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetPersistentPresenceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetPersistentPresenceJabberImpl.java index 69c168c..c502824 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetPersistentPresenceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetPersistentPresenceJabberImpl.java @@ -691,7 +691,7 @@ public class OperationSetPersistentPresenceJabberImpl */ assertConnected(); - XMPPConnection xmppConnection = parentProvider.getConnection(); + Connection xmppConnection = parentProvider.getConnection(); if (xmppConnection == null) { @@ -1124,7 +1124,7 @@ public class OperationSetPersistentPresenceJabberImpl ssContactList.cleanup(); - XMPPConnection connection = parentProvider.getConnection(); + Connection connection = parentProvider.getConnection(); if(connection != null) { connection.removePacketListener(subscribtionPacketListener); @@ -1528,6 +1528,19 @@ public class OperationSetPersistentPresenceJabberImpl o2, parentProvider).getStatus() - jabberStatusToPresenceStatus( o1, parentProvider).getStatus(); + // We have run out of "logical" ways to order + // the presences inside the TreeSet. We have + // make sure we are consinstent with equals. + // We do this by comparing the unique resource + // names. If this evaluates to 0 again, then we + // can safely assume this presence object + // represents the same resource and by that the + // same client. + if(res == 0) + { + res = o1.getFrom().compareTo( + o2.getFrom()); + } } return res; diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java index e81b43a..e5b83e7 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,585 +15,585 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.coin.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.service.protocol.media.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.util.xml.*;
-import org.jivesoftware.smack.*;
-import org.jivesoftware.smack.filter.*;
-import org.jivesoftware.smack.packet.*;
-import org.jivesoftware.smack.packet.IQ.Type;
-import org.jivesoftware.smack.util.*;
-import org.jivesoftware.smackx.packet.*;
-
-/**
- * Implements <tt>OperationSetTelephonyConferencing</tt> for Jabber.
- *
- * @author Lyubomir Marinov
- * @author Sebastien Vincent
- * @author Boris Grozev
- * @author Pawel Domas
- */
-public class OperationSetTelephonyConferencingJabberImpl
- extends AbstractOperationSetTelephonyConferencing<
- ProtocolProviderServiceJabberImpl,
- OperationSetBasicTelephonyJabberImpl,
- CallJabberImpl,
- CallPeerJabberImpl,
- String>
- implements RegistrationStateChangeListener,
- PacketListener,
- PacketFilter
-
-{
- /**
- * The <tt>Logger</tt> used by the
- * <tt>OperationSetTelephonyConferencingJabberImpl</tt> class and its
- * instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(OperationSetTelephonyConferencingJabberImpl.class);
-
- /**
- * The minimum interval in milliseconds between COINs sent to a single
- * <tt>CallPeer</tt>.
- */
- private static final int COIN_MIN_INTERVAL = 200;
-
- /**
- * Property used to disable COIN notifications.
- */
- public static final String DISABLE_COIN_PROP_NAME
- = "net.java.sip.communicator.impl.protocol.jabber.DISABLE_COIN";
-
- /**
- * Synchronization object.
- */
- private final Object lock = new Object();
-
- /**
- * Field indicates whether COIN notification are disabled or not.
- */
- private boolean isCoinDisabled = false;
-
- /**
- * Initializes a new <tt>OperationSetTelephonyConferencingJabberImpl</tt>
- * instance which is to provide telephony conferencing services for the
- * specified Jabber <tt>ProtocolProviderService</tt> implementation.
- *
- * @param parentProvider the Jabber <tt>ProtocolProviderService</tt>
- * implementation which has requested the creation of the new instance and
- * for which the new instance is to provide telephony conferencing services
- */
- public OperationSetTelephonyConferencingJabberImpl(
- ProtocolProviderServiceJabberImpl parentProvider)
- {
- super(parentProvider);
-
- this.isCoinDisabled
- = JabberActivator.getConfigurationService()
- .getBoolean(DISABLE_COIN_PROP_NAME, false);
- }
-
- /**
- * Notifies all <tt>CallPeer</tt>s associated with a specific <tt>Call</tt>
- * about changes in the telephony conference-related information. In
- * contrast, {@link #notifyAll()} notifies all <tt>CallPeer</tt>s associated
- * with the telephony conference in which a specific <tt>Call</tt> is
- * participating.
- *
- * @param call the <tt>Call</tt> whose <tt>CallPeer</tt>s are to be notified
- * about changes in the telephony conference-related information
- */
- @Override
- protected void notifyCallPeers(Call call)
- {
- if (!isCoinDisabled && call.isConferenceFocus())
- {
- synchronized (lock)
- {
- // send conference-info to all CallPeers of the specified call.
- for (Iterator<? extends CallPeer> i = call.getCallPeers();
- i.hasNext();)
- {
- notify(i.next());
- }
- }
- }
- }
-
- /**
- * Notifies a specific <tt>CallPeer</tt> about changes in the telephony
- * conference-related information.
- *
- * @param callPeer the <tt>CallPeer</tt> to notify.
- */
- private void notify(CallPeer callPeer)
- {
- if(!(callPeer instanceof CallPeerJabberImpl))
- return;
-
- //Don't send COINs to peers with might not be ready to accept COINs yet
- CallPeerState peerState = callPeer.getState();
- if (peerState == CallPeerState.CONNECTING
- || peerState == CallPeerState.UNKNOWN
- || peerState == CallPeerState.INITIATING_CALL
- || peerState == CallPeerState.DISCONNECTED
- || peerState == CallPeerState.FAILED)
- return;
-
- final CallPeerJabberImpl callPeerJabber = (CallPeerJabberImpl)callPeer;
-
- final long timeSinceLastCoin = System.currentTimeMillis()
- - callPeerJabber.getLastConferenceInfoSentTimestamp();
- if (timeSinceLastCoin < COIN_MIN_INTERVAL)
- {
- if (callPeerJabber.isConfInfoScheduled())
- return;
-
- logger.info("Scheduling to send a COIN to " + callPeerJabber);
- callPeerJabber.setConfInfoScheduled(true);
- new Thread(new Runnable(){
- @Override
- public void run()
- {
- try
- {
- Thread.sleep(1 + COIN_MIN_INTERVAL - timeSinceLastCoin);
- }
- catch (InterruptedException ie) {}
-
- OperationSetTelephonyConferencingJabberImpl.this
- .notify(callPeerJabber);
- }
- }).start();
-
- return;
- }
-
- // check that callPeer supports COIN before sending him a
- // conference-info
- String to = getBasicTelephony().getFullCalleeURI(callPeer.getAddress());
-
- // XXX if this generates actual disco#info requests we might want to
- // cache it.
- try
- {
- DiscoverInfo discoverInfo
- = parentProvider.getDiscoveryManager().discoverInfo(to);
-
- if (!discoverInfo.containsFeature(
- ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_COIN))
- {
- logger.info(callPeer.getAddress() + " does not support COIN");
- callPeerJabber.setConfInfoScheduled(false);
- return;
- }
- }
- catch (XMPPException xmppe)
- {
- logger.warn("Failed to retrieve DiscoverInfo for " + to, xmppe);
- }
-
- ConferenceInfoDocument currentConfInfo
- = getCurrentConferenceInfo(callPeerJabber);
- ConferenceInfoDocument lastSentConfInfo
- = callPeerJabber.getLastConferenceInfoSent();
-
- ConferenceInfoDocument diff;
-
- if (lastSentConfInfo == null)
- diff = currentConfInfo;
- else
- diff = getConferenceInfoDiff(lastSentConfInfo, currentConfInfo);
-
- if (diff != null)
- {
- int newVersion
- = lastSentConfInfo == null
- ? 1
- : lastSentConfInfo.getVersion() + 1;
- diff.setVersion(newVersion);
-
- IQ iq = getConferenceInfo(callPeerJabber, diff);
-
- if (iq != null)
- {
- parentProvider.getConnection().sendPacket(iq);
-
- // We save currentConfInfo, because it is of state "full", while
- // diff could be a partial
- currentConfInfo.setVersion(newVersion);
- callPeerJabber.setLastConferenceInfoSent(currentConfInfo);
- callPeerJabber.setLastConferenceInfoSentTimestamp(
- System.currentTimeMillis());
- }
- }
- callPeerJabber.setConfInfoScheduled(false);
- }
-
- /**
- * Generates the conference-info IQ to be sent to a specific
- * <tt>CallPeer</tt> in order to notify it of the current state of the
- * conference managed by the local peer.
- *
- * @param callPeer the <tt>CallPeer</tt> to generate conference-info XML for
- * @param confInfo the <tt>ConferenceInformationDocument</tt> which is to be
- * included in the IQ
- * @return the conference-info IQ to be sent to the specified
- * <tt>callPeer</tt> in order to notify it of the current state of the
- * conference managed by the local peer
- */
- private IQ getConferenceInfo(CallPeerJabberImpl callPeer,
- final ConferenceInfoDocument confInfo)
- {
- String callPeerSID = callPeer.getSID();
-
- if (callPeerSID == null)
- return null;
-
- IQ iq = new IQ(){
- @Override
- public String getChildElementXML()
- {
- return confInfo.toXml();
- }
- };
-
- CallJabberImpl call = callPeer.getCall();
-
- iq.setFrom(call.getProtocolProvider().getOurJID());
- iq.setTo(callPeer.getAddress());
- iq.setType(Type.SET);
-
- return iq;
- }
-
- /**
- * Implementation of method <tt>registrationStateChange</tt> from
- * interface RegistrationStateChangeListener for setting up (or down)
- * our <tt>JingleManager</tt> when an <tt>XMPPConnection</tt> is available
- *
- * @param evt the event received
- */
- @Override
- public void registrationStateChanged(RegistrationStateChangeEvent evt)
- {
- super.registrationStateChanged(evt);
-
- RegistrationState registrationState = evt.getNewState();
-
- if (RegistrationState.REGISTERED.equals(registrationState))
- {
- if(logger.isDebugEnabled())
- logger.debug("Subscribes to Coin packets");
- subscribeForCoinPackets();
- }
- else if (RegistrationState.UNREGISTERED.equals(registrationState))
- {
- if(logger.isDebugEnabled())
- logger.debug("Unsubscribes to Coin packets");
- unsubscribeForCoinPackets();
- }
- }
-
- /**
- * Creates a new outgoing <tt>Call</tt> into which conference callees are to
- * be invited by this <tt>OperationSetTelephonyConferencing</tt>.
- *
- * @return a new outgoing <tt>Call</tt> into which conference callees are to
- * be invited by this <tt>OperationSetTelephonyConferencing</tt>
- * @throws OperationFailedException if anything goes wrong
- */
- @Override
- protected CallJabberImpl createOutgoingCall()
- throws OperationFailedException
- {
- return new CallJabberImpl(getBasicTelephony());
- }
-
- /**
- * {@inheritDoc}
- *
- * Implements the protocol-dependent part of the logic of inviting a callee
- * to a <tt>Call</tt>. The protocol-independent part of that logic is
- * implemented by
- * {@link AbstractOperationSetTelephonyConferencing#inviteCalleeToCall(String,Call)}.
- */
- @Override
- protected CallPeer doInviteCalleeToCall(
- String calleeAddress,
- CallJabberImpl call)
- throws OperationFailedException
- {
- return
- getBasicTelephony().createOutgoingCall(
- call,
- calleeAddress,
- Arrays.asList(
- new PacketExtension[]
- {
- new CoinPacketExtension(true)
- }));
- }
-
- /**
- * Parses a <tt>String</tt> value which represents a callee address
- * specified by the user into an object which is to actually represent the
- * callee during the invitation to a conference <tt>Call</tt>.
- *
- * @param calleeAddressString a <tt>String</tt> value which represents a
- * callee address to be parsed into an object which is to actually represent
- * the callee during the invitation to a conference <tt>Call</tt>
- * @return an object which is to actually represent the specified
- * <tt>calleeAddressString</tt> during the invitation to a conference
- * <tt>Call</tt>
- * @throws OperationFailedException if parsing the specified
- * <tt>calleeAddressString</tt> fails
- */
- @Override
- protected String parseAddressString(String calleeAddressString)
- throws OperationFailedException
- {
- return getBasicTelephony().getFullCalleeURI(calleeAddressString);
- }
-
- /**
- * Subscribes us to notifications about incoming Coin packets.
- */
- private void subscribeForCoinPackets()
- {
- parentProvider.getConnection().addPacketListener(this, this);
- }
-
- /**
- * Unsubscribes us from notifications about incoming Coin packets.
- */
- private void unsubscribeForCoinPackets()
- {
- XMPPConnection connection = parentProvider.getConnection();
-
- if (connection != null)
- connection.removePacketListener(this);
- }
-
- /**
- * Tests whether or not the specified packet should be handled by this
- * operation set. This method is called by smack prior to packet delivery
- * and it would only accept <tt>CoinIQ</tt>s.
- *
- * @param packet the packet to test.
- * @return true if and only if <tt>packet</tt> passes the filter.
- */
- public boolean accept(Packet packet)
- {
- return (packet instanceof CoinIQ);
- }
-
- /**
- * Handles incoming jingle packets and passes them to the corresponding
- * method based on their action.
- *
- * @param packet the packet to process.
- */
- public void processPacket(Packet packet)
- {
- CoinIQ coinIQ = (CoinIQ) packet;
- String errorMessage = null;
-
- //first ack all "set" requests.
- IQ.Type type = coinIQ.getType();
- if (type == IQ.Type.SET)
- {
- IQ ack = IQ.createResultIQ(coinIQ);
-
- parentProvider.getConnection().sendPacket(ack);
- }
- else if(type == IQ.Type.ERROR)
- {
- XMPPError error = coinIQ.getError();
- if(error != null)
- {
- String msg = error.getMessage();
- errorMessage = ((msg != null)? (msg + " ") : "")
- + "Error code: " + error.getCode();
- }
-
- logger.error("Received error in COIN packet. "+errorMessage);
- }
-
- String sid = coinIQ.getSID();
-
- if (sid != null)
- {
- CallPeerJabberImpl callPeer
- = getBasicTelephony().getActiveCallsRepository().findCallPeer(
- sid);
-
-
- if (callPeer != null)
- {
- if(type == IQ.Type.ERROR)
- {
- callPeer.fireConferenceMemberErrorEvent(errorMessage);
- return;
- }
-
- if (logger.isDebugEnabled())
- logger.debug("Processing COIN from " + coinIQ.getFrom()
- + " (version=" + coinIQ.getVersion() + ")");
-
- handleCoin(callPeer, coinIQ);
- }
- }
- }
-
- /**
- * Handles a specific <tt>CoinIQ</tt> sent from a specific
- * <tt>CallPeer</tt>.
- *
- * @param callPeer the <tt>CallPeer</tt> from which the specified
- * <tt>CoinIQ</tt> was sent
- * @param coinIQ the <tt>CoinIQ</tt> which was sent from the specified
- * <tt>callPeer</tt>
- */
- private void handleCoin(CallPeerJabberImpl callPeer, CoinIQ coinIQ)
- {
- try
- {
- setConferenceInfoXML(callPeer, coinIQ.getChildElementXML());
- }
- catch (XMLException e)
- {
- logger.error("Could not handle received COIN from " + callPeer
- + ": " + coinIQ);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * For COINs (XEP-0298), we use the attributes of the
- * <tt>conference-info</tt> element to piggyback a Jingle SID. This is
- * temporary and should be removed once we choose a better way to pass the
- * SID.
- */
- @Override
- protected ConferenceInfoDocument getCurrentConferenceInfo(
- MediaAwareCallPeer<?,?,?> callPeer)
- {
- ConferenceInfoDocument confInfo
- = super.getCurrentConferenceInfo(callPeer);
-
- if (callPeer instanceof CallPeerJabberImpl
- && confInfo != null)
- {
- confInfo.setSid(((CallPeerJabberImpl)callPeer).getSID());
- }
- return confInfo;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected String getLocalEntity(CallPeer callPeer)
- {
- JingleIQ sessionIQ = ((CallPeerJabberImpl)callPeer).getSessionIQ();
- String from = sessionIQ.getFrom();
- String chatRoomName = StringUtils.parseBareAddress(from);
- OperationSetMultiUserChatJabberImpl opSetMUC
- = (OperationSetMultiUserChatJabberImpl)
- parentProvider.getOperationSet(OperationSetMultiUserChat.class);
- ChatRoom room = null;
- if(opSetMUC != null)
- room = opSetMUC.getChatRoom(chatRoomName);
-
- if(room != null)
- return "xmpp:" + chatRoomName + "/" + room.getUserNickname();
-
- return "xmpp:" + parentProvider.getOurJID();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected String getLocalDisplayName()
- {
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * The URI of the returned <tt>ConferenceDescription</tt> is the occupant
- * JID with which we have joined the room.
- *
- * If a Videobridge is available for our <tt>ProtocolProviderService</tt>
- * we use it. TODO: this should be relaxed when we refactor the Videobridge
- * implementation, so that any Videobridge (on any protocol provider) can
- * be used.
- */
- @Override
- public ConferenceDescription setupConference(final ChatRoom chatRoom)
- {
- OperationSetVideoBridge videoBridge
- = parentProvider.getOperationSet(OperationSetVideoBridge.class);
- boolean isVideobridge = (videoBridge != null) && videoBridge.isActive();
-
- CallJabberImpl call = new CallJabberImpl(getBasicTelephony());
- call.setAutoAnswer(true);
-
- String uri = "xmpp:" + chatRoom.getIdentifier() +
- "/" + chatRoom.getUserNickname();
-
- ConferenceDescription cd
- = new ConferenceDescription(uri, call.getCallID());
-
- call.addCallChangeListener(new CallChangeListener()
- {
- @Override
- public void callStateChanged(CallChangeEvent ev)
- {
- if(CallState.CALL_ENDED.equals(ev.getNewValue()))
- chatRoom.publishConference(null, null);
- }
-
- @Override
- public void callPeerRemoved(CallPeerEvent ev)
- {
- }
-
- @Override
- public void callPeerAdded(CallPeerEvent ev)
- {
- }
- });
- if (isVideobridge)
- {
- call.setConference(new MediaAwareCallConference(true));
-
- //For Jitsi Videobridge we set the transports to RAW-UDP, otherwise
- //we leave them empty (meaning both RAW-UDP and ICE could be used)
- cd.addTransport(
- ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0);
- }
-
- if (logger.isInfoEnabled())
- {
- logger.info("Setup a conference with uri=" + uri + " and callid=" +
- call.getCallID() + ". Videobridge in use: " + isVideobridge);
- }
-
- return cd;
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber; + +import java.util.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.coin.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.protocol.media.*; +import net.java.sip.communicator.util.*; + +import org.jitsi.util.xml.*; +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.util.*; +import org.jivesoftware.smackx.packet.*; + +/** + * Implements <tt>OperationSetTelephonyConferencing</tt> for Jabber. + * + * @author Lyubomir Marinov + * @author Sebastien Vincent + * @author Boris Grozev + * @author Pawel Domas + */ +public class OperationSetTelephonyConferencingJabberImpl + extends AbstractOperationSetTelephonyConferencing< + ProtocolProviderServiceJabberImpl, + OperationSetBasicTelephonyJabberImpl, + CallJabberImpl, + CallPeerJabberImpl, + String> + implements RegistrationStateChangeListener, + PacketListener, + PacketFilter + +{ + /** + * The <tt>Logger</tt> used by the + * <tt>OperationSetTelephonyConferencingJabberImpl</tt> class and its + * instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(OperationSetTelephonyConferencingJabberImpl.class); + + /** + * The minimum interval in milliseconds between COINs sent to a single + * <tt>CallPeer</tt>. + */ + private static final int COIN_MIN_INTERVAL = 200; + + /** + * Property used to disable COIN notifications. + */ + public static final String DISABLE_COIN_PROP_NAME + = "net.java.sip.communicator.impl.protocol.jabber.DISABLE_COIN"; + + /** + * Synchronization object. + */ + private final Object lock = new Object(); + + /** + * Field indicates whether COIN notification are disabled or not. + */ + private boolean isCoinDisabled = false; + + /** + * Initializes a new <tt>OperationSetTelephonyConferencingJabberImpl</tt> + * instance which is to provide telephony conferencing services for the + * specified Jabber <tt>ProtocolProviderService</tt> implementation. + * + * @param parentProvider the Jabber <tt>ProtocolProviderService</tt> + * implementation which has requested the creation of the new instance and + * for which the new instance is to provide telephony conferencing services + */ + public OperationSetTelephonyConferencingJabberImpl( + ProtocolProviderServiceJabberImpl parentProvider) + { + super(parentProvider); + + this.isCoinDisabled + = JabberActivator.getConfigurationService() + .getBoolean(DISABLE_COIN_PROP_NAME, false); + } + + /** + * Notifies all <tt>CallPeer</tt>s associated with a specific <tt>Call</tt> + * about changes in the telephony conference-related information. In + * contrast, {@link #notifyAll()} notifies all <tt>CallPeer</tt>s associated + * with the telephony conference in which a specific <tt>Call</tt> is + * participating. + * + * @param call the <tt>Call</tt> whose <tt>CallPeer</tt>s are to be notified + * about changes in the telephony conference-related information + */ + @Override + protected void notifyCallPeers(Call call) + { + if (!isCoinDisabled && call.isConferenceFocus()) + { + synchronized (lock) + { + // send conference-info to all CallPeers of the specified call. + for (Iterator<? extends CallPeer> i = call.getCallPeers(); + i.hasNext();) + { + notify(i.next()); + } + } + } + } + + /** + * Notifies a specific <tt>CallPeer</tt> about changes in the telephony + * conference-related information. + * + * @param callPeer the <tt>CallPeer</tt> to notify. + */ + private void notify(CallPeer callPeer) + { + if(!(callPeer instanceof CallPeerJabberImpl)) + return; + + //Don't send COINs to peers with might not be ready to accept COINs yet + CallPeerState peerState = callPeer.getState(); + if (peerState == CallPeerState.CONNECTING + || peerState == CallPeerState.UNKNOWN + || peerState == CallPeerState.INITIATING_CALL + || peerState == CallPeerState.DISCONNECTED + || peerState == CallPeerState.FAILED) + return; + + final CallPeerJabberImpl callPeerJabber = (CallPeerJabberImpl)callPeer; + + final long timeSinceLastCoin = System.currentTimeMillis() + - callPeerJabber.getLastConferenceInfoSentTimestamp(); + if (timeSinceLastCoin < COIN_MIN_INTERVAL) + { + if (callPeerJabber.isConfInfoScheduled()) + return; + + logger.info("Scheduling to send a COIN to " + callPeerJabber); + callPeerJabber.setConfInfoScheduled(true); + new Thread(new Runnable(){ + @Override + public void run() + { + try + { + Thread.sleep(1 + COIN_MIN_INTERVAL - timeSinceLastCoin); + } + catch (InterruptedException ie) {} + + OperationSetTelephonyConferencingJabberImpl.this + .notify(callPeerJabber); + } + }).start(); + + return; + } + + // check that callPeer supports COIN before sending him a + // conference-info + String to = getBasicTelephony().getFullCalleeURI(callPeer.getAddress()); + + // XXX if this generates actual disco#info requests we might want to + // cache it. + try + { + DiscoverInfo discoverInfo + = parentProvider.getDiscoveryManager().discoverInfo(to); + + if (!discoverInfo.containsFeature( + ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_COIN)) + { + logger.info(callPeer.getAddress() + " does not support COIN"); + callPeerJabber.setConfInfoScheduled(false); + return; + } + } + catch (XMPPException xmppe) + { + logger.warn("Failed to retrieve DiscoverInfo for " + to, xmppe); + } + + ConferenceInfoDocument currentConfInfo + = getCurrentConferenceInfo(callPeerJabber); + ConferenceInfoDocument lastSentConfInfo + = callPeerJabber.getLastConferenceInfoSent(); + + ConferenceInfoDocument diff; + + if (lastSentConfInfo == null) + diff = currentConfInfo; + else + diff = getConferenceInfoDiff(lastSentConfInfo, currentConfInfo); + + if (diff != null) + { + int newVersion + = lastSentConfInfo == null + ? 1 + : lastSentConfInfo.getVersion() + 1; + diff.setVersion(newVersion); + + IQ iq = getConferenceInfo(callPeerJabber, diff); + + if (iq != null) + { + parentProvider.getConnection().sendPacket(iq); + + // We save currentConfInfo, because it is of state "full", while + // diff could be a partial + currentConfInfo.setVersion(newVersion); + callPeerJabber.setLastConferenceInfoSent(currentConfInfo); + callPeerJabber.setLastConferenceInfoSentTimestamp( + System.currentTimeMillis()); + } + } + callPeerJabber.setConfInfoScheduled(false); + } + + /** + * Generates the conference-info IQ to be sent to a specific + * <tt>CallPeer</tt> in order to notify it of the current state of the + * conference managed by the local peer. + * + * @param callPeer the <tt>CallPeer</tt> to generate conference-info XML for + * @param confInfo the <tt>ConferenceInformationDocument</tt> which is to be + * included in the IQ + * @return the conference-info IQ to be sent to the specified + * <tt>callPeer</tt> in order to notify it of the current state of the + * conference managed by the local peer + */ + private IQ getConferenceInfo(CallPeerJabberImpl callPeer, + final ConferenceInfoDocument confInfo) + { + String callPeerSID = callPeer.getSID(); + + if (callPeerSID == null) + return null; + + IQ iq = new IQ(){ + @Override + public String getChildElementXML() + { + return confInfo.toXml(); + } + }; + + CallJabberImpl call = callPeer.getCall(); + + iq.setFrom(call.getProtocolProvider().getOurJID()); + iq.setTo(callPeer.getAddress()); + iq.setType(Type.SET); + + return iq; + } + + /** + * Implementation of method <tt>registrationStateChange</tt> from + * interface RegistrationStateChangeListener for setting up (or down) + * our <tt>JingleManager</tt> when an <tt>XMPPConnection</tt> is available + * + * @param evt the event received + */ + @Override + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + super.registrationStateChanged(evt); + + RegistrationState registrationState = evt.getNewState(); + + if (RegistrationState.REGISTERED.equals(registrationState)) + { + if(logger.isDebugEnabled()) + logger.debug("Subscribes to Coin packets"); + subscribeForCoinPackets(); + } + else if (RegistrationState.UNREGISTERED.equals(registrationState)) + { + if(logger.isDebugEnabled()) + logger.debug("Unsubscribes to Coin packets"); + unsubscribeForCoinPackets(); + } + } + + /** + * Creates a new outgoing <tt>Call</tt> into which conference callees are to + * be invited by this <tt>OperationSetTelephonyConferencing</tt>. + * + * @return a new outgoing <tt>Call</tt> into which conference callees are to + * be invited by this <tt>OperationSetTelephonyConferencing</tt> + * @throws OperationFailedException if anything goes wrong + */ + @Override + protected CallJabberImpl createOutgoingCall() + throws OperationFailedException + { + return new CallJabberImpl(getBasicTelephony()); + } + + /** + * {@inheritDoc} + * + * Implements the protocol-dependent part of the logic of inviting a callee + * to a <tt>Call</tt>. The protocol-independent part of that logic is + * implemented by + * {@link AbstractOperationSetTelephonyConferencing#inviteCalleeToCall(String,Call)}. + */ + @Override + protected CallPeer doInviteCalleeToCall( + String calleeAddress, + CallJabberImpl call) + throws OperationFailedException + { + return + getBasicTelephony().createOutgoingCall( + call, + calleeAddress, + Arrays.asList( + new PacketExtension[] + { + new CoinPacketExtension(true) + })); + } + + /** + * Parses a <tt>String</tt> value which represents a callee address + * specified by the user into an object which is to actually represent the + * callee during the invitation to a conference <tt>Call</tt>. + * + * @param calleeAddressString a <tt>String</tt> value which represents a + * callee address to be parsed into an object which is to actually represent + * the callee during the invitation to a conference <tt>Call</tt> + * @return an object which is to actually represent the specified + * <tt>calleeAddressString</tt> during the invitation to a conference + * <tt>Call</tt> + * @throws OperationFailedException if parsing the specified + * <tt>calleeAddressString</tt> fails + */ + @Override + protected String parseAddressString(String calleeAddressString) + throws OperationFailedException + { + return getBasicTelephony().getFullCalleeURI(calleeAddressString); + } + + /** + * Subscribes us to notifications about incoming Coin packets. + */ + private void subscribeForCoinPackets() + { + parentProvider.getConnection().addPacketListener(this, this); + } + + /** + * Unsubscribes us from notifications about incoming Coin packets. + */ + private void unsubscribeForCoinPackets() + { + Connection connection = parentProvider.getConnection(); + + if (connection != null) + connection.removePacketListener(this); + } + + /** + * Tests whether or not the specified packet should be handled by this + * operation set. This method is called by smack prior to packet delivery + * and it would only accept <tt>CoinIQ</tt>s. + * + * @param packet the packet to test. + * @return true if and only if <tt>packet</tt> passes the filter. + */ + public boolean accept(Packet packet) + { + return (packet instanceof CoinIQ); + } + + /** + * Handles incoming jingle packets and passes them to the corresponding + * method based on their action. + * + * @param packet the packet to process. + */ + public void processPacket(Packet packet) + { + CoinIQ coinIQ = (CoinIQ) packet; + String errorMessage = null; + + //first ack all "set" requests. + IQ.Type type = coinIQ.getType(); + if (type == IQ.Type.SET) + { + IQ ack = IQ.createResultIQ(coinIQ); + + parentProvider.getConnection().sendPacket(ack); + } + else if(type == IQ.Type.ERROR) + { + XMPPError error = coinIQ.getError(); + if(error != null) + { + String msg = error.getMessage(); + errorMessage = ((msg != null)? (msg + " ") : "") + + "Error code: " + error.getCode(); + } + + logger.error("Received error in COIN packet. "+errorMessage); + } + + String sid = coinIQ.getSID(); + + if (sid != null) + { + CallPeerJabberImpl callPeer + = getBasicTelephony().getActiveCallsRepository().findCallPeer( + sid); + + + if (callPeer != null) + { + if(type == IQ.Type.ERROR) + { + callPeer.fireConferenceMemberErrorEvent(errorMessage); + return; + } + + if (logger.isDebugEnabled()) + logger.debug("Processing COIN from " + coinIQ.getFrom() + + " (version=" + coinIQ.getVersion() + ")"); + + handleCoin(callPeer, coinIQ); + } + } + } + + /** + * Handles a specific <tt>CoinIQ</tt> sent from a specific + * <tt>CallPeer</tt>. + * + * @param callPeer the <tt>CallPeer</tt> from which the specified + * <tt>CoinIQ</tt> was sent + * @param coinIQ the <tt>CoinIQ</tt> which was sent from the specified + * <tt>callPeer</tt> + */ + private void handleCoin(CallPeerJabberImpl callPeer, CoinIQ coinIQ) + { + try + { + setConferenceInfoXML(callPeer, coinIQ.getChildElementXML()); + } + catch (XMLException e) + { + logger.error("Could not handle received COIN from " + callPeer + + ": " + coinIQ); + } + } + + /** + * {@inheritDoc} + * + * For COINs (XEP-0298), we use the attributes of the + * <tt>conference-info</tt> element to piggyback a Jingle SID. This is + * temporary and should be removed once we choose a better way to pass the + * SID. + */ + @Override + protected ConferenceInfoDocument getCurrentConferenceInfo( + MediaAwareCallPeer<?,?,?> callPeer) + { + ConferenceInfoDocument confInfo + = super.getCurrentConferenceInfo(callPeer); + + if (callPeer instanceof CallPeerJabberImpl + && confInfo != null) + { + confInfo.setSid(((CallPeerJabberImpl)callPeer).getSID()); + } + return confInfo; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getLocalEntity(CallPeer callPeer) + { + JingleIQ sessionIQ = ((CallPeerJabberImpl)callPeer).getSessionIQ(); + String from = sessionIQ.getFrom(); + String chatRoomName = StringUtils.parseBareAddress(from); + OperationSetMultiUserChatJabberImpl opSetMUC + = (OperationSetMultiUserChatJabberImpl) + parentProvider.getOperationSet(OperationSetMultiUserChat.class); + ChatRoom room = null; + if(opSetMUC != null) + room = opSetMUC.getChatRoom(chatRoomName); + + if(room != null) + return "xmpp:" + chatRoomName + "/" + room.getUserNickname(); + + return "xmpp:" + parentProvider.getOurJID(); + } + + /** + * {@inheritDoc} + */ + @Override + protected String getLocalDisplayName() + { + return null; + } + + /** + * {@inheritDoc} + * + * The URI of the returned <tt>ConferenceDescription</tt> is the occupant + * JID with which we have joined the room. + * + * If a Videobridge is available for our <tt>ProtocolProviderService</tt> + * we use it. TODO: this should be relaxed when we refactor the Videobridge + * implementation, so that any Videobridge (on any protocol provider) can + * be used. + */ + @Override + public ConferenceDescription setupConference(final ChatRoom chatRoom) + { + OperationSetVideoBridge videoBridge + = parentProvider.getOperationSet(OperationSetVideoBridge.class); + boolean isVideobridge = (videoBridge != null) && videoBridge.isActive(); + + CallJabberImpl call = new CallJabberImpl(getBasicTelephony()); + call.setAutoAnswer(true); + + String uri = "xmpp:" + chatRoom.getIdentifier() + + "/" + chatRoom.getUserNickname(); + + ConferenceDescription cd + = new ConferenceDescription(uri, call.getCallID()); + + call.addCallChangeListener(new CallChangeListener() + { + @Override + public void callStateChanged(CallChangeEvent ev) + { + if(CallState.CALL_ENDED.equals(ev.getNewValue())) + chatRoom.publishConference(null, null); + } + + @Override + public void callPeerRemoved(CallPeerEvent ev) + { + } + + @Override + public void callPeerAdded(CallPeerEvent ev) + { + } + }); + if (isVideobridge) + { + call.setConference(new MediaAwareCallConference(true)); + + //For Jitsi Videobridge we set the transports to RAW-UDP, otherwise + //we leave them empty (meaning both RAW-UDP and ICE could be used) + cd.addTransport( + ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0); + } + + if (logger.isInfoEnabled()) + { + logger.info("Setup a conference with uri=" + uri + " and callid=" + + call.getCallID() + ". Videobridge in use: " + isVideobridge); + } + + return cd; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java index b7e13ea..5d3dd8b 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,282 +15,282 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.service.protocol.media.*;
-import net.java.sip.communicator.util.*;
-
-import org.jivesoftware.smack.*;
-import org.jivesoftware.smack.filter.*;
-import org.jivesoftware.smack.packet.*;
-
-/**
- * Implements <tt>OperationSetVideoBridge</tt> for Jabber.
- *
- * @author Yana Stamcheva
- * @author Lyubomir Marinov
- */
-public class OperationSetVideoBridgeImpl
- implements OperationSetVideoBridge,
- PacketFilter,
- PacketListener,
- RegistrationStateChangeListener
-{
- /**
- * The <tt>Logger</tt> used by the <tt>OperationSetVideoBridgeImpl</tt>
- * class and its instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(OperationSetVideoBridgeImpl.class);
-
- /**
- * The <tt>ProtocolProviderService</tt> implementation which initialized
- * this instance, owns it and is often referred to as its parent.
- */
- private final ProtocolProviderServiceJabberImpl protocolProvider;
-
- /**
- * Creates an instance of <tt>OperationSetVideoBridgeImpl</tt> by
- * specifying the parent <tt>ProtocolProviderService</tt> announcing this
- * operation set.
- *
- * @param protocolProvider the parent Jabber protocol provider
- */
- public OperationSetVideoBridgeImpl(
- ProtocolProviderServiceJabberImpl protocolProvider)
- {
- this.protocolProvider = protocolProvider;
- this.protocolProvider.addRegistrationStateChangeListener(this);
- }
-
- /**
- * Implements {@link PacketFilter}. Determines whether this instance is
- * interested in a specific {@link Packet}.
- * <tt>OperationSetVideoBridgeImpl</tt> returns <tt>true</tt> if the
- * specified <tt>packet</tt> is a {@link ColibriConferenceIQ}; otherwise,
- * <tt>false</tt>.
- *
- * @param packet the <tt>Packet</tt> to be determined whether this instance
- * is interested in it
- * @return <tt>true</tt> if the specified <tt>packet</tt> is a
- * <tt>ColibriConferenceIQ</tt>; otherwise, <tt>false</tt>
- */
- public boolean accept(Packet packet)
- {
- return (packet instanceof ColibriConferenceIQ);
- }
-
- /**
- * Creates a conference call with the specified callees as call peers via a
- * video bridge provided by the parent Jabber provider.
- *
- * @param callees the list of addresses that we should call
- * @return the newly created conference call containing all CallPeers
- * @throws OperationFailedException if establishing the conference call
- * fails
- * @throws OperationNotSupportedException if the provider does not have any
- * conferencing features.
- */
- public Call createConfCall(String[] callees)
- throws OperationFailedException,
- OperationNotSupportedException
- {
- return
- protocolProvider
- .getOperationSet(OperationSetTelephonyConferencing.class)
- .createConfCall(
- callees,
- new MediaAwareCallConference(true));
- }
-
- /**
- * Invites the callee represented by the specified uri to an already
- * existing call using a video bridge provided by the parent Jabber provider.
- * The difference between this method and createConfCall is that
- * inviteCalleeToCall allows a user to add new peers to an already
- * established conference.
- *
- * @param uri the callee to invite to an existing conf call.
- * @param call the call that we should invite the callee to.
- * @return the CallPeer object corresponding to the callee represented by
- * the specified uri.
- * @throws OperationFailedException if inviting the specified callee to the
- * specified call fails
- * @throws OperationNotSupportedException if allowing additional callees to
- * a pre-established call is not supported.
- */
- public CallPeer inviteCalleeToCall(String uri, Call call)
- throws OperationFailedException,
- OperationNotSupportedException
- {
- return
- protocolProvider
- .getOperationSet(OperationSetTelephonyConferencing.class)
- .inviteCalleeToCall(uri, call);
- }
-
- /**
- * Indicates if there's an active video bridge available at this moment. The
- * Jabber provider may announce support for video bridge, but it should not
- * be used for calling until it becomes actually active.
- *
- * @return <tt>true</tt> to indicate that there's currently an active
- * available video bridge, <tt>false</tt> - otherwise
- */
- public boolean isActive()
- {
- String jitsiVideobridge = protocolProvider.getJitsiVideobridge();
-
- return ((jitsiVideobridge != null) && (jitsiVideobridge.length() > 0));
- }
-
- /**
- * Notifies this instance that a specific <tt>ColibriConferenceIQ</tt> has
- * been received.
- *
- * @param conferenceIQ the <tt>ColibriConferenceIQ</tt> which has been
- * received
- */
- private void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ)
- {
- /*
- * The application is not a Jitsi Videobridge server, it is a client.
- * Consequently, the specified ColibriConferenceIQ is sent to it in
- * relation to the part of the application's functionality which makes
- * requests to a Jitsi Videobridge server i.e. CallJabberImpl.
- *
- * Additionally, the method processColibriConferenceIQ is presently tasked
- * with processing ColibriConferenceIQ requests only. They are SET IQs
- * sent by the Jitsi Videobridge server to notify the application about
- * updates in the states of (colibri) conferences organized by the
- * application.
- */
- if (IQ.Type.SET.equals(conferenceIQ.getType())
- && conferenceIQ.getID() != null)
- {
- OperationSetBasicTelephony<?> basicTelephony
- = protocolProvider.getOperationSet(
- OperationSetBasicTelephony.class);
-
- if (basicTelephony != null)
- {
- Iterator<? extends Call> i = basicTelephony.getActiveCalls();
-
- while (i.hasNext())
- {
- Call call = i.next();
-
- if (call instanceof CallJabberImpl)
- {
- CallJabberImpl callJabberImpl = (CallJabberImpl) call;
- MediaAwareCallConference conference
- = callJabberImpl.getConference();
-
- if ((conference != null)
- && conference.isJitsiVideobridge())
- {
- /*
- * TODO We may want to disallow rogue CallJabberImpl
- * instances which may throw an exception to prevent
- * the conferenceIQ from reaching the CallJabberImpl
- * instance which it was meant for.
- */
- if (callJabberImpl.processColibriConferenceIQ(
- conferenceIQ))
- break;
- }
- }
- }
- }
- }
- }
-
- /**
- * Implements {@link PacketListener}. Notifies this instance that a specific
- * {@link Packet} (which this instance has already expressed interest into
- * by returning <tt>true</tt> from {@link #accept(Packet)}) has been
- * received.
- *
- * @param packet the <tt>Packet</tt> which has been received and which this
- * instance is given a chance to process
- */
- public void processPacket(Packet packet)
- {
- /*
- * As we do elsewhere, acknowledge the receipt of the Packet first and
- * then go about our business with it.
- */
- IQ iq = (IQ) packet;
-
- if (iq.getType() == IQ.Type.SET)
- protocolProvider.getConnection().sendPacket(IQ.createResultIQ(iq));
-
- /*
- * Now that the acknowledging is out of the way, do go about our
- * business with the Packet.
- */
- ColibriConferenceIQ conferenceIQ = (ColibriConferenceIQ) iq;
- boolean interrupted = false;
-
- try
- {
- processColibriConferenceIQ(conferenceIQ);
- }
- catch (Throwable t)
- {
- logger.error(
- "An error occurred during the processing of a "
- + packet.getClass().getName() + " packet",
- t);
-
- if (t instanceof InterruptedException)
- {
- /*
- * We cleared the interrupted state of the current Thread by
- * catching the InterruptedException. However, we do not really
- * care whether the current Thread has been interrupted - we
- * caught the InterruptedException because we want to swallow
- * any Throwable. Consequently, we should better restore the
- * interrupted state.
- */
- interrupted = true;
- }
- else if (t instanceof ThreadDeath)
- throw (ThreadDeath) t;
- }
- if (interrupted)
- Thread.currentThread().interrupt();
- }
-
- /**
- * {@inheritDoc}
- *
- * Implements {@link RegistrationStateChangeListener}. Notifies this
- * instance that there has been a change in the <tt>RegistrationState</tt>
- * of {@link #protocolProvider}. Subscribes this instance to
- * {@link ColibriConferenceIQ}s as soon as <tt>protocolProvider</tt> is
- * registered and unsubscribes it as soon as <tt>protocolProvider</tt> is
- * unregistered.
- */
- public void registrationStateChanged(RegistrationStateChangeEvent ev)
- {
- RegistrationState registrationState = ev.getNewState();
-
- if (RegistrationState.REGISTERED.equals(registrationState))
- {
- protocolProvider.getConnection().addPacketListener(this, this);
- }
- else if (RegistrationState.UNREGISTERED.equals(registrationState))
- {
- XMPPConnection connection = protocolProvider.getConnection();
-
- if (connection != null)
- connection.removePacketListener(this);
- }
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber; + +import java.util.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.protocol.media.*; +import net.java.sip.communicator.util.*; + +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; + +/** + * Implements <tt>OperationSetVideoBridge</tt> for Jabber. + * + * @author Yana Stamcheva + * @author Lyubomir Marinov + */ +public class OperationSetVideoBridgeImpl + implements OperationSetVideoBridge, + PacketFilter, + PacketListener, + RegistrationStateChangeListener +{ + /** + * The <tt>Logger</tt> used by the <tt>OperationSetVideoBridgeImpl</tt> + * class and its instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(OperationSetVideoBridgeImpl.class); + + /** + * The <tt>ProtocolProviderService</tt> implementation which initialized + * this instance, owns it and is often referred to as its parent. + */ + private final ProtocolProviderServiceJabberImpl protocolProvider; + + /** + * Creates an instance of <tt>OperationSetVideoBridgeImpl</tt> by + * specifying the parent <tt>ProtocolProviderService</tt> announcing this + * operation set. + * + * @param protocolProvider the parent Jabber protocol provider + */ + public OperationSetVideoBridgeImpl( + ProtocolProviderServiceJabberImpl protocolProvider) + { + this.protocolProvider = protocolProvider; + this.protocolProvider.addRegistrationStateChangeListener(this); + } + + /** + * Implements {@link PacketFilter}. Determines whether this instance is + * interested in a specific {@link Packet}. + * <tt>OperationSetVideoBridgeImpl</tt> returns <tt>true</tt> if the + * specified <tt>packet</tt> is a {@link ColibriConferenceIQ}; otherwise, + * <tt>false</tt>. + * + * @param packet the <tt>Packet</tt> to be determined whether this instance + * is interested in it + * @return <tt>true</tt> if the specified <tt>packet</tt> is a + * <tt>ColibriConferenceIQ</tt>; otherwise, <tt>false</tt> + */ + public boolean accept(Packet packet) + { + return (packet instanceof ColibriConferenceIQ); + } + + /** + * Creates a conference call with the specified callees as call peers via a + * video bridge provided by the parent Jabber provider. + * + * @param callees the list of addresses that we should call + * @return the newly created conference call containing all CallPeers + * @throws OperationFailedException if establishing the conference call + * fails + * @throws OperationNotSupportedException if the provider does not have any + * conferencing features. + */ + public Call createConfCall(String[] callees) + throws OperationFailedException, + OperationNotSupportedException + { + return + protocolProvider + .getOperationSet(OperationSetTelephonyConferencing.class) + .createConfCall( + callees, + new MediaAwareCallConference(true)); + } + + /** + * Invites the callee represented by the specified uri to an already + * existing call using a video bridge provided by the parent Jabber provider. + * The difference between this method and createConfCall is that + * inviteCalleeToCall allows a user to add new peers to an already + * established conference. + * + * @param uri the callee to invite to an existing conf call. + * @param call the call that we should invite the callee to. + * @return the CallPeer object corresponding to the callee represented by + * the specified uri. + * @throws OperationFailedException if inviting the specified callee to the + * specified call fails + * @throws OperationNotSupportedException if allowing additional callees to + * a pre-established call is not supported. + */ + public CallPeer inviteCalleeToCall(String uri, Call call) + throws OperationFailedException, + OperationNotSupportedException + { + return + protocolProvider + .getOperationSet(OperationSetTelephonyConferencing.class) + .inviteCalleeToCall(uri, call); + } + + /** + * Indicates if there's an active video bridge available at this moment. The + * Jabber provider may announce support for video bridge, but it should not + * be used for calling until it becomes actually active. + * + * @return <tt>true</tt> to indicate that there's currently an active + * available video bridge, <tt>false</tt> - otherwise + */ + public boolean isActive() + { + String jitsiVideobridge = protocolProvider.getJitsiVideobridge(); + + return ((jitsiVideobridge != null) && (jitsiVideobridge.length() > 0)); + } + + /** + * Notifies this instance that a specific <tt>ColibriConferenceIQ</tt> has + * been received. + * + * @param conferenceIQ the <tt>ColibriConferenceIQ</tt> which has been + * received + */ + private void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ) + { + /* + * The application is not a Jitsi Videobridge server, it is a client. + * Consequently, the specified ColibriConferenceIQ is sent to it in + * relation to the part of the application's functionality which makes + * requests to a Jitsi Videobridge server i.e. CallJabberImpl. + * + * Additionally, the method processColibriConferenceIQ is presently tasked + * with processing ColibriConferenceIQ requests only. They are SET IQs + * sent by the Jitsi Videobridge server to notify the application about + * updates in the states of (colibri) conferences organized by the + * application. + */ + if (IQ.Type.SET.equals(conferenceIQ.getType()) + && conferenceIQ.getID() != null) + { + OperationSetBasicTelephony<?> basicTelephony + = protocolProvider.getOperationSet( + OperationSetBasicTelephony.class); + + if (basicTelephony != null) + { + Iterator<? extends Call> i = basicTelephony.getActiveCalls(); + + while (i.hasNext()) + { + Call call = i.next(); + + if (call instanceof CallJabberImpl) + { + CallJabberImpl callJabberImpl = (CallJabberImpl) call; + MediaAwareCallConference conference + = callJabberImpl.getConference(); + + if ((conference != null) + && conference.isJitsiVideobridge()) + { + /* + * TODO We may want to disallow rogue CallJabberImpl + * instances which may throw an exception to prevent + * the conferenceIQ from reaching the CallJabberImpl + * instance which it was meant for. + */ + if (callJabberImpl.processColibriConferenceIQ( + conferenceIQ)) + break; + } + } + } + } + } + } + + /** + * Implements {@link PacketListener}. Notifies this instance that a specific + * {@link Packet} (which this instance has already expressed interest into + * by returning <tt>true</tt> from {@link #accept(Packet)}) has been + * received. + * + * @param packet the <tt>Packet</tt> which has been received and which this + * instance is given a chance to process + */ + public void processPacket(Packet packet) + { + /* + * As we do elsewhere, acknowledge the receipt of the Packet first and + * then go about our business with it. + */ + IQ iq = (IQ) packet; + + if (iq.getType() == IQ.Type.SET) + protocolProvider.getConnection().sendPacket(IQ.createResultIQ(iq)); + + /* + * Now that the acknowledging is out of the way, do go about our + * business with the Packet. + */ + ColibriConferenceIQ conferenceIQ = (ColibriConferenceIQ) iq; + boolean interrupted = false; + + try + { + processColibriConferenceIQ(conferenceIQ); + } + catch (Throwable t) + { + logger.error( + "An error occurred during the processing of a " + + packet.getClass().getName() + " packet", + t); + + if (t instanceof InterruptedException) + { + /* + * We cleared the interrupted state of the current Thread by + * catching the InterruptedException. However, we do not really + * care whether the current Thread has been interrupted - we + * caught the InterruptedException because we want to swallow + * any Throwable. Consequently, we should better restore the + * interrupted state. + */ + interrupted = true; + } + else if (t instanceof ThreadDeath) + throw (ThreadDeath) t; + } + if (interrupted) + Thread.currentThread().interrupt(); + } + + /** + * {@inheritDoc} + * + * Implements {@link RegistrationStateChangeListener}. Notifies this + * instance that there has been a change in the <tt>RegistrationState</tt> + * of {@link #protocolProvider}. Subscribes this instance to + * {@link ColibriConferenceIQ}s as soon as <tt>protocolProvider</tt> is + * registered and unsubscribes it as soon as <tt>protocolProvider</tt> is + * unregistered. + */ + public void registrationStateChanged(RegistrationStateChangeEvent ev) + { + RegistrationState registrationState = ev.getNewState(); + + if (RegistrationState.REGISTERED.equals(registrationState)) + { + protocolProvider.getConnection().addPacketListener(this, this); + } + else if (RegistrationState.UNREGISTERED.equals(registrationState)) + { + Connection connection = protocolProvider.getConnection(); + + if (connection != null) + connection.removePacketListener(this); + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java index f38d0bc..b219d68 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java @@ -248,7 +248,7 @@ public class OutgoingFileTransferJabberImpl ThumbnailIQ thumbnailIQ = (ThumbnailIQ) packet; String thumbnailIQCid = thumbnailIQ.getCid(); - XMPPConnection connection = protocolProvider.getConnection(); + Connection connection = protocolProvider.getConnection(); if ((thumbnailIQCid != null) && thumbnailIQCid.equals(thumbnailElement.getCid())) diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java index 4f9fc5f..86666f3 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java @@ -21,6 +21,7 @@ import java.util.*; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.jabber.*; import org.jivesoftware.smack.provider.*; import org.jivesoftware.smack.util.*; import org.osgi.framework.*; @@ -46,7 +47,14 @@ public class ProtocolProviderFactoryJabberImpl { try { + + // Set the extension provider manager for classes that use + // it directly ProviderManager.setInstance(new ProviderManagerExt()); + // Set the Smack interop implementation for the classes that need + // to support Smackv4 interoperation + AbstractSmackInteroperabilityLayer.setImplementationClass( + SmackV3InteroperabilityLayer.class); } catch(Throwable t) { @@ -179,7 +187,7 @@ public class ProtocolProviderFactoryJabberImpl ProtocolProviderServiceJabberImpl service = new ProtocolProviderServiceJabberImpl(); - service.initialize(userID, accountID); + service.initialize(userID, (JabberAccountID) accountID); return service; } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java index 2ea5c9c..47b9b75 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java @@ -32,6 +32,7 @@ import net.java.sip.communicator.impl.protocol.jabber.extensions.carbon.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.coin.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jibri.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.keepalive.*; @@ -41,6 +42,7 @@ import net.java.sip.communicator.service.certificate.*; import net.java.sip.communicator.service.dns.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.protocol.jabber.*; import net.java.sip.communicator.service.protocol.jabberconstants.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.Logger; @@ -152,6 +154,14 @@ public class ProtocolProviderServiceJabberImpl public static final String URN_IETF_RFC_3264 = "urn:ietf:rfc:3264"; /** + * http://xmpp.org/extensions/xep-0092.html Software Version. + * + */ + // Used in JVB + @SuppressWarnings("unused") + public static final String URN_XMPP_IQ_VERSION = "jabber:iq:version"; + + /** * Jingle's Discovery Info URN for "XEP-0294: Jingle RTP Header Extensions * Negotiation" support. */ @@ -211,7 +221,7 @@ public class ProtocolProviderServiceJabberImpl /** * Used to connect to a XMPP server. */ - private XMPPConnection connection; + private Connection connection; /** * The socket address of the XMPP server. @@ -231,7 +241,7 @@ public class ProtocolProviderServiceJabberImpl /** * The identifier of the account that this provider represents. */ - private AccountID accountID = null; + private JabberAccountID accountID = null; /** * Used when we need to re-register @@ -598,7 +608,7 @@ public class ProtocolProviderServiceJabberImpl */ public boolean isSignalingTransportSecure() { - return connection != null && connection.isUsingTLS(); + return connection.isSecureConnection(); } /** @@ -613,7 +623,7 @@ public class ProtocolProviderServiceJabberImpl if(connection != null && connection.isConnected()) { // Transport using a secure connection. - if(connection.isUsingTLS()) + if(isSignalingTransportSecure()) { return TransportProtocol.TLS; } @@ -1112,11 +1122,24 @@ public class ProtocolProviderServiceJabberImpl JabberLoginStrategy loginStrategy) throws XMPPException { - ConnectionConfiguration confConn = new ConnectionConfiguration( - address.getAddress().getHostAddress(), - address.getPort(), - serviceName, proxy - ); + // BOSH or TCP ? + ConnectionConfiguration confConn; + String boshURL = accountID.getBoshUrl(); + boolean isBosh = !org.jitsi.util.StringUtils.isNullOrEmpty(boshURL); + + if (isBosh) + { + confConn = new BOSHConfiguration(serviceName); + ((BOSHConfiguration)confConn).setBoshUrl(boshURL); + } + else + { + confConn + = new ConnectionConfiguration( + address.getAddress().getHostAddress(), + address.getPort(), + serviceName, proxy); + } // if we have OperationSetPersistentPresence skip sending initial // presence while login is executed, the OperationSet will take care @@ -1144,7 +1167,11 @@ public class ProtocolProviderServiceJabberImpl disconnectAndCleanConnection(); } - connection = new XMPPConnection(confConn); + connection + = isBosh + ? new XMPPBOSHConnection((BOSHConfiguration)confConn) + : new XMPPConnection(confConn); + this.address = address; try @@ -1194,13 +1221,16 @@ public class ProtocolProviderServiceJabberImpl throw new XMPPException("Error creating custom trust manager", e); } - if(debugger == null) + // FIXME rework debugger to work with Connection if possible + if(debugger == null && connection instanceof XMPPConnection) + { debugger = new SmackPacketDebugger(); - // sets the debugger - debugger.setConnection(connection); - connection.addPacketListener(debugger, null); - connection.addPacketInterceptor(debugger, null); + // sets the debugger + debugger.setConnection((XMPPConnection) connection); + connection.addPacketListener(debugger, null); + connection.addPacketInterceptor(debugger, null); + } connection.connect(); @@ -1247,9 +1277,10 @@ public class ProtocolProviderServiceJabberImpl } else { - if (connection.getSocket() instanceof SSLSocket) + final SSLSocket sslSocket = getSSLSocket(); + + if (sslSocket != null) { - final SSLSocket sslSocket = (SSLSocket) connection.getSocket(); StringBuilder buff = new StringBuilder(); buff.append("Chosen TLS protocol and algorithm:\n") .append("Protocol: ").append(sslSocket.getSession() @@ -1538,7 +1569,7 @@ public class ProtocolProviderServiceJabberImpl * @see net.java.sip.communicator.service.protocol.AccountID */ protected void initialize(String screenname, - AccountID accountID) + JabberAccountID accountID) { synchronized(initializationLock) { @@ -1732,6 +1763,12 @@ public class ProtocolProviderServiceJabberImpl ColibriConferenceIQ.NAMESPACE, new ColibriIQProvider()); + providerManager.addIQProvider( + JibriIq.ELEMENT_NAME, + JibriIq.NAMESPACE, + new JibriIqProvider() + ); + providerManager.addExtensionProvider( ConferenceDescriptionPacketExtension.ELEMENT_NAME, ConferenceDescriptionPacketExtension.NAMESPACE, @@ -2025,11 +2062,98 @@ public class ProtocolProviderServiceJabberImpl } /** - * Returns the <tt>XMPPConnection</tt>opened by this provider - * @return a reference to the <tt>XMPPConnection</tt> last opened by this + * Validates the node part of a JID and returns an error message if + * applicable and a suggested correction. + * + * @param contactId the contact identifier to validate + * @param result Must be supplied as an empty a list. Implementors add + * items: + * <ol> + * <li>is the error message if applicable + * <li>a suggested correction. Index 1 is optional and can only + * be present if there was a validation failure. + * </ol> + * @return true if the contact id is valid, false otherwise + */ + @Override + public boolean validateContactAddress(String contactId, List<String> result) + { + if (result == null) + { + throw new IllegalArgumentException("result must be an empty list"); + } + + result.clear(); + try + { + contactId = contactId.trim(); + if (contactId.length() == 0) + { + result.add(JabberActivator.getResources().getI18NString( + "impl.protocol.jabber.INVALID_ADDRESS", new String[] + { contactId })); + // no suggestion for an empty id + return false; + } + + String user = contactId; + String remainder = ""; + int at = contactId.indexOf('@'); + if (at > -1) + { + user = contactId.substring(0, at); + remainder = contactId.substring(at); + } + + // <conforming-char> ::= #x21 | [#x23-#x25] | [#x28-#x2E] | + // [#x30-#x39] | #x3B | #x3D | #x3F | + // [#x41-#x7E] | [#x80-#xD7FF] | + // [#xE000-#xFFFD] | [#x10000-#x10FFFF] + boolean valid = true; + String suggestion = ""; + for (char c : user.toCharArray()) + { + if (!(c == 0x21 || (c >= 0x23 && c <= 0x25) + || (c >= 0x28 && c <= 0x2e) || (c >= 0x30 && c <= 0x39) + || c == 0x3b || c == 0x3d || c == 0x3f + || (c >= 0x41 && c <= 0x7e) || (c >= 0x80 && c <= 0xd7ff) + || (c >= 0xe000 && c <= 0xfffd))) + { + valid = false; + } + else + { + suggestion += c; + } + } + + if (!valid) + { + result.add(JabberActivator.getResources().getI18NString( + "impl.protocol.jabber.INVALID_ADDRESS", new String[] + { contactId })); + result.add(suggestion + remainder); + return false; + } + + return true; + } + catch (Exception ex) + { + result.add(JabberActivator.getResources().getI18NString( + "impl.protocol.jabber.INVALID_ADDRESS", new String[] + { contactId })); + } + + return false; + } + + /** + * Returns the <tt>Connection</tt>opened by this provider + * @return a reference to the <tt>Connection</tt> last opened by this * provider. */ - public XMPPConnection getConnection() + public Connection getConnection() { return connection; } @@ -2315,18 +2439,16 @@ public class ProtocolProviderServiceJabberImpl */ public boolean isFeatureListSupported(String jid, String... features) { - boolean isFeatureListSupported = true; - try { if(discoveryManager == null) - return isFeatureListSupported; + return false; DiscoverInfo featureInfo = discoveryManager.discoverInfoNonBlocking(jid); if(featureInfo == null) - return isFeatureListSupported; + return false; for (String feature : features) { @@ -2334,17 +2456,19 @@ public class ProtocolProviderServiceJabberImpl { // If one is not supported we return false and don't check // the others. - isFeatureListSupported = false; - break; + return false; } } + + return true; } catch (XMPPException e) { if (logger.isDebugEnabled()) logger.debug("Failed to retrive discovery info.", e); } - return isFeatureListSupported; + + return false; } /** @@ -2386,7 +2510,7 @@ public class ProtocolProviderServiceJabberImpl */ public String getFullJid(String bareJid) { - XMPPConnection connection = getConnection(); + Connection connection = getConnection(); // when we are not connected there is no full jid if (connection != null && connection.isConnected()) @@ -2590,12 +2714,21 @@ public class ProtocolProviderServiceJabberImpl */ public void startJingleNodesDiscovery() { + if (!(connection instanceof XMPPConnection)) + { + logger.warn( + "Jingle node discovery currently will work only with " + + "TCP XMPP connection"); + return; + } + // Jingle Nodes Service Initialization + final XMPPConnection xmppConnection = (XMPPConnection) connection; final JabberAccountIDImpl accID = (JabberAccountIDImpl)getAccountID(); - final SmackServiceNode service = new SmackServiceNode(connection, - 60000); + final SmackServiceNode service + = new SmackServiceNode(xmppConnection, 60000); // make sure SmackServiceNode will clean up when connection is closed - connection.addConnectionListener(service); + xmppConnection.addConnectionListener(service); for(JingleNodeDescriptor desc : accID.getJingleNodes()) { @@ -2611,7 +2744,7 @@ public class ProtocolProviderServiceJabberImpl new Thread(new JingleNodesServiceDiscovery( service, - connection, + xmppConnection, accID, jingleNodesSyncRoot)) .start(); @@ -2739,7 +2872,7 @@ public class ProtocolProviderServiceJabberImpl */ private void setTrafficClass() { - Socket s = connection.getSocket(); + Socket s = getSocket(); if(s != null) { @@ -2774,7 +2907,7 @@ public class ProtocolProviderServiceJabberImpl */ public String getJitsiVideobridge() { - XMPPConnection connection = getConnection(); + Connection connection = getConnection(); if (connection != null) { @@ -2865,21 +2998,23 @@ public class ProtocolProviderServiceJabberImpl } /** + * Obtains XMPP connection's socket. + * @return <tt>Socket</tt> instance used by the underlying XMPP connection + * or <tt>null</tt> if "non socket" type of transport is currently used. + */ + private Socket getSocket() + { + return connection != null ? connection.getSocket() : null; + } + + /** * Return the SSL socket (if TLS used). * @return The SSL socket or null if not used */ - public SSLSocket getSSLSocket() + SSLSocket getSSLSocket() { - final SSLSocket result; - final Socket socket = connection.getSocket(); - if (socket instanceof SSLSocket) - { - result = (SSLSocket) socket; - } - else - { - result = null; - } - return result; + final Socket socket = getSocket(); + + return (socket instanceof SSLSocket) ? (SSLSocket) socket : null; } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java index 0aa4fb6..4bd49a9 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,529 +15,529 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.net.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
-import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
-import net.java.sip.communicator.service.protocol.*;
-
-import org.jitsi.service.neomedia.*;
-import org.jivesoftware.smack.packet.*;
-
-/**
- * A {@link TransportManagerJabberImpl} implementation that would only gather a
- * single candidate pair (i.e. RTP and RTCP).
- *
- * @author Emil Ivov
- * @author Lyubomir Marinov
- * @author Hristo Terezov
- */
-public class RawUdpTransportManager
- extends TransportManagerJabberImpl
-{
- /**
- * The list of <tt>ContentPacketExtension</tt>s which represents the local
- * counterpart of the negotiation between the local and the remote peers.
- */
- private List<ContentPacketExtension> local;
-
- /**
- * The collection of <tt>ContentPacketExtension</tt>s which represents the
- * remote counterpart of the negotiation between the local and the remote
- * peers.
- */
- private final List<Iterable<ContentPacketExtension>> remotes
- = new LinkedList<Iterable<ContentPacketExtension>>();
-
- /**
- * Creates a new instance of this transport manager, binding it to the
- * specified peer.
- *
- * @param callPeer the {@link CallPeer} whose traffic we will be taking
- * care of.
- */
- public RawUdpTransportManager(CallPeerJabberImpl callPeer)
- {
- super(callPeer);
- }
-
- /**
- * {@inheritDoc}
- */
- protected PacketExtension createTransport(String media)
- throws OperationFailedException
- {
- MediaType mediaType = MediaType.parseString(media);
-
- return createTransport(mediaType, getStreamConnector(mediaType));
- }
-
- /**
- * Creates a raw UDP transport element according to a specific
- * <tt>StreamConnector</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
- * uses the specified <tt>connector</tt> or <tt>channel</tt>
- * @param connector the <tt>StreamConnector</tt> to be described within the
- * transport element
- * @return a {@link RawUdpTransportPacketExtension} containing the RTP and
- * RTCP candidates of the specified <tt>connector</tt>
- */
- private RawUdpTransportPacketExtension createTransport(
- MediaType mediaType,
- StreamConnector connector)
- {
- RawUdpTransportPacketExtension ourTransport
- = new RawUdpTransportPacketExtension();
- int generation = getCurrentGeneration();
-
- // create and add candidates that correspond to the stream connector
- // RTP
- CandidatePacketExtension rtpCand = new CandidatePacketExtension();
-
- rtpCand.setComponent(CandidatePacketExtension.RTP_COMPONENT_ID);
- rtpCand.setGeneration(generation);
- rtpCand.setID(getNextID());
- rtpCand.setType(CandidateType.host);
-
- DatagramSocket dataSocket = connector.getDataSocket();
-
- rtpCand.setIP(dataSocket.getLocalAddress().getHostAddress());
- rtpCand.setPort(dataSocket.getLocalPort());
-
- ourTransport.addCandidate(rtpCand);
-
- // RTCP
- CandidatePacketExtension rtcpCand = new CandidatePacketExtension();
-
- rtcpCand.setComponent(CandidatePacketExtension.RTCP_COMPONENT_ID);
- rtcpCand.setGeneration(generation);
- rtcpCand.setID(getNextID());
- rtcpCand.setType(CandidateType.host);
-
- DatagramSocket controlSocket = connector.getControlSocket();
-
- rtcpCand.setIP(controlSocket.getLocalAddress().getHostAddress());
- rtcpCand.setPort(controlSocket.getLocalPort());
-
- ourTransport.addCandidate(rtcpCand);
-
- return ourTransport;
- }
-
- /**
- * {@inheritDoc}
- */
- protected PacketExtension createTransportPacketExtension()
- {
- return new RawUdpTransportPacketExtension();
- }
-
- /**
- * Implements {@link TransportManagerJabberImpl#getStreamTarget(MediaType)}.
- * Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of
- * the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
- * is to have its <tt>target</tt> set to the returned
- * <tt>MediaStreamTarget</tt>
- * @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt>
- * of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
- * @see TransportManagerJabberImpl#getStreamTarget(MediaType)
- */
- @Override
- public MediaStreamTarget getStreamTarget(MediaType mediaType)
- {
- ColibriConferenceIQ.Channel channel
- = getColibriChannel(mediaType, true /* local */);
- MediaStreamTarget streamTarget = null;
-
- if (channel == null)
- {
- String media = mediaType.toString();
-
- for (Iterable<ContentPacketExtension> remote : remotes)
- {
- for (ContentPacketExtension content : remote)
- {
- RtpDescriptionPacketExtension rtpDescription
- = content.getFirstChildOfType(
- RtpDescriptionPacketExtension.class);
-
- if (media.equals(rtpDescription.getMedia()))
- {
- streamTarget
- = JingleUtils.extractDefaultTarget(content);
- break;
- }
- }
- }
- }
- else
- {
- IceUdpTransportPacketExtension transport = channel.getTransport();
-
- if (transport != null)
- streamTarget = JingleUtils.extractDefaultTarget(transport);
- if (streamTarget == null)
- {
- /*
- * For the purposes of compatibility with legacy Jitsi
- * Videobridge, support the channel attributes host, rtpPort and
- * rtcpPort.
- */
- @SuppressWarnings("deprecation")
- String host = channel.getHost();
-
- if (host != null)
- {
- @SuppressWarnings("deprecation")
- int rtpPort = channel.getRTPPort();
- @SuppressWarnings("deprecation")
- int rtcpPort = channel.getRTCPPort();
-
- streamTarget
- = new MediaStreamTarget(
- new InetSocketAddress(host, rtpPort),
- new InetSocketAddress(host, rtcpPort));
- }
- }
- }
- return streamTarget;
- }
-
- /**
- * Implements {@link TransportManagerJabberImpl#getXmlNamespace()}. Gets the
- * XML namespace of the Jingle transport implemented by this
- * <tt>TransportManagerJabberImpl</tt>.
- *
- * @return the XML namespace of the Jingle transport implemented by this
- * <tt>TransportManagerJabberImpl</tt>
- * @see TransportManagerJabberImpl#getXmlNamespace()
- */
- @Override
- public String getXmlNamespace()
- {
- return ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0;
- }
-
- /**
- * Removes a content with a specific name from the transport-related part of
- * the session represented by this <tt>TransportManagerJabberImpl</tt> which
- * may have been reported through previous calls to the
- * <tt>startCandidateHarvest</tt> and
- * <tt>startConnectivityEstablishment</tt> methods.
- *
- * @param name the name of the content to be removed from the
- * transport-related part of the session represented by this
- * <tt>TransportManagerJabberImpl</tt>
- * @see TransportManagerJabberImpl#removeContent(String)
- */
- @Override
- public void removeContent(String name)
- {
- if (local != null)
- removeContent(local, name);
-
- removeRemoteContent(name);
- }
-
- /**
- * Removes a content with a specific name from the remote counterpart of the
- * negotiation between the local and the remote peers.
- *
- * @param name the name of the content to be removed from the remote
- * counterpart of the negotiation between the local and the remote peers
- */
- private void removeRemoteContent(String name)
- {
- for (Iterator<Iterable<ContentPacketExtension>> remoteIter
- = remotes.iterator();
- remoteIter.hasNext();)
- {
- Iterable<ContentPacketExtension> remote = remoteIter.next();
-
- /*
- * Once the remote content is removed, make sure that we are not
- * retaining sets which do not have any contents.
- */
- if ((removeContent(remote, name) != null)
- && !remote.iterator().hasNext())
- {
- remoteIter.remove();
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- protected PacketExtension startCandidateHarvest(
- ContentPacketExtension theirContent,
- ContentPacketExtension ourContent,
- TransportInfoSender transportInfoSender,
- String media)
- throws OperationFailedException
- {
- return createTransportForStartCandidateHarvest(media);
- }
-
- /**
- * Starts transport candidate harvest. This method should complete rapidly
- * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
- * are necessary, they should be executed in a separate thread. Candidate
- * harvest would then need to be concluded in the
- * {@link #wrapupCandidateHarvest()} method which would be called once we
- * absolutely need the candidates.
- *
- * @param theirOffer a media description offer that we've received from the
- * remote party and that we should use in case we need to know what
- * transports our peer is using.
- * @param ourAnswer the content descriptions that we should be adding our
- * transport lists to (although not necessarily in this very instance).
- * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
- * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
- * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
- * <tt>TransportManagerJabberImpl</tt> wishes to utilize
- * <tt>transport-info</tt>. Local candidate addresses sent by this
- * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
- * expected to not be included in the result of
- * {@link #wrapupCandidateHarvest()}.
- *
- * @throws OperationFailedException if we fail to allocate a port number.
- * @see TransportManagerJabberImpl#startCandidateHarvest(List, List,
- * TransportInfoSender)
- */
- @Override
- public void startCandidateHarvest(
- List<ContentPacketExtension> theirOffer,
- List<ContentPacketExtension> ourAnswer,
- TransportInfoSender transportInfoSender)
- throws OperationFailedException
- {
- this.local = ourAnswer;
-
- super.startCandidateHarvest(theirOffer, ourAnswer, transportInfoSender);
- }
-
- /**
- * Overrides the super implementation in order to remember the remote
- * counterpart of the negotiation between the local and the remote peer for
- * subsequent calls to {@link #getStreamTarget(MediaType)}.
- *
- * @param remote the collection of <tt>ContentPacketExtension</tt>s which
- * represents the remote counterpart of the negotiation between the local
- * and the remote peer
- * @return <tt>true</tt> because <tt>RawUdpTransportManager</tt> does not
- * perform connectivity checks
- * @see TransportManagerJabberImpl#startConnectivityEstablishment(Iterable)
- */
- @Override
- public boolean startConnectivityEstablishment(
- Iterable<ContentPacketExtension> remote)
- {
- if ((remote != null) && !remotes.contains(remote))
- {
- /*
- * The state of the session in Jingle is maintained by each peer and
- * is modified by content-add and content-remove. The remotes field
- * of this RawUdpTransportManager represents the state of the
- * session with respect to the remote peer. When the remote peer
- * tells us about a specific set of contents, make sure that it is
- * the only record we will have with respect to the specified set of
- * contents.
- */
- for (ContentPacketExtension content : remote)
- removeRemoteContent(content.getName());
-
- remotes.add(remote);
- }
-
- return super.startConnectivityEstablishment(remote);
- }
-
- /**
- * Simply returns the list of local candidates that we gathered during the
- * harvest. This is a raw UDP transport manager so there's no real wrapping
- * up to do.
- *
- * @return the list of local candidates that we gathered during the harvest
- * @see TransportManagerJabberImpl#wrapupCandidateHarvest()
- */
- @Override
- public List<ContentPacketExtension> wrapupCandidateHarvest()
- {
- return local;
- }
-
- /**
- * Returns the extended type of the candidate selected if this transport
- * manager is using ICE.
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return The extended type of the candidate selected if this transport
- * manager is using ICE. Otherwise, returns null.
- */
- @Override
- public String getICECandidateExtendedType(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the current state of ICE processing.
- *
- * @return the current state of ICE processing.
- */
- @Override
- public String getICEState()
- {
- return null;
- }
-
- /**
- * Returns the ICE local host address.
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE local host address if this transport
- * manager is using ICE. Otherwise, returns null.
- */
- @Override
- public InetSocketAddress getICELocalHostAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE remote host address.
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE remote host address if this transport
- * manager is using ICE. Otherwise, returns null.
- */
- @Override
- public InetSocketAddress getICERemoteHostAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE local reflexive address (server or peer reflexive).
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE local reflexive address. May be null if this transport
- * manager is not using ICE or if there is no reflexive address for the
- * local candidate used.
- */
- @Override
- public InetSocketAddress getICELocalReflexiveAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE remote reflexive address (server or peer reflexive).
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE remote reflexive address. May be null if this transport
- * manager is not using ICE or if there is no reflexive address for the
- * remote candidate used.
- */
- @Override
- public InetSocketAddress getICERemoteReflexiveAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE local relayed address (server or peer relayed).
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE local relayed address. May be null if this transport
- * manager is not using ICE or if there is no relayed address for the
- * local candidate used.
- */
- @Override
- public InetSocketAddress getICELocalRelayedAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE remote relayed address (server or peer relayed).
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE remote relayed address. May be null if this transport
- * manager is not using ICE or if there is no relayed address for the
- * remote candidate used.
- */
- @Override
- public InetSocketAddress getICERemoteRelayedAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the total harvesting time (in ms) for all harvesters.
- *
- * @return The total harvesting time (in ms) for all the harvesters. 0 if
- * the ICE agent is null, or if the agent has nevers harvested.
- */
- @Override
- public long getTotalHarvestingTime()
- {
- return 0;
- }
-
- /**
- * Returns the harvesting time (in ms) for the harvester given in parameter.
- *
- * @param harvesterName The class name if the harvester.
- *
- * @return The harvesting time (in ms) for the harvester given in parameter.
- * 0 if this harvester does not exists, if the ICE agent is null, or if the
- * agent has never harvested with this harvester.
- */
- @Override
- public long getHarvestingTime(String harvesterName)
- {
- return 0;
- }
-
- /**
- * Returns the number of harvesting for this agent.
- *
- * @return The number of harvesting for this agent.
- */
- @Override
- public int getNbHarvesting()
- {
- return 0;
- }
-
- /**
- * Returns the number of harvesting time for the harvester given in
- * parameter.
- *
- * @param harvesterName The class name if the harvester.
- *
- * @return The number of harvesting time for the harvester given in
- * parameter.
- */
- @Override
- public int getNbHarvesting(String harvesterName)
- {
- return 0;
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber; + +import java.net.*; +import java.util.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*; +import net.java.sip.communicator.service.protocol.*; + +import org.jitsi.service.neomedia.*; +import org.jivesoftware.smack.packet.*; + +/** + * A {@link TransportManagerJabberImpl} implementation that would only gather a + * single candidate pair (i.e. RTP and RTCP). + * + * @author Emil Ivov + * @author Lyubomir Marinov + * @author Hristo Terezov + */ +public class RawUdpTransportManager + extends TransportManagerJabberImpl +{ + /** + * The list of <tt>ContentPacketExtension</tt>s which represents the local + * counterpart of the negotiation between the local and the remote peers. + */ + private List<ContentPacketExtension> local; + + /** + * The collection of <tt>ContentPacketExtension</tt>s which represents the + * remote counterpart of the negotiation between the local and the remote + * peers. + */ + private final List<Iterable<ContentPacketExtension>> remotes + = new LinkedList<Iterable<ContentPacketExtension>>(); + + /** + * Creates a new instance of this transport manager, binding it to the + * specified peer. + * + * @param callPeer the {@link CallPeer} whose traffic we will be taking + * care of. + */ + public RawUdpTransportManager(CallPeerJabberImpl callPeer) + { + super(callPeer); + } + + /** + * {@inheritDoc} + */ + protected PacketExtension createTransport(String media) + throws OperationFailedException + { + MediaType mediaType = MediaType.parseString(media); + + return createTransport(mediaType, getStreamConnector(mediaType)); + } + + /** + * Creates a raw UDP transport element according to a specific + * <tt>StreamConnector</tt>. + * + * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which + * uses the specified <tt>connector</tt> or <tt>channel</tt> + * @param connector the <tt>StreamConnector</tt> to be described within the + * transport element + * @return a {@link RawUdpTransportPacketExtension} containing the RTP and + * RTCP candidates of the specified <tt>connector</tt> + */ + private RawUdpTransportPacketExtension createTransport( + MediaType mediaType, + StreamConnector connector) + { + RawUdpTransportPacketExtension ourTransport + = new RawUdpTransportPacketExtension(); + int generation = getCurrentGeneration(); + + // create and add candidates that correspond to the stream connector + // RTP + CandidatePacketExtension rtpCand = new CandidatePacketExtension(); + + rtpCand.setComponent(CandidatePacketExtension.RTP_COMPONENT_ID); + rtpCand.setGeneration(generation); + rtpCand.setID(getNextID()); + rtpCand.setType(CandidateType.host); + + DatagramSocket dataSocket = connector.getDataSocket(); + + rtpCand.setIP(dataSocket.getLocalAddress().getHostAddress()); + rtpCand.setPort(dataSocket.getLocalPort()); + + ourTransport.addCandidate(rtpCand); + + // RTCP + CandidatePacketExtension rtcpCand = new CandidatePacketExtension(); + + rtcpCand.setComponent(CandidatePacketExtension.RTCP_COMPONENT_ID); + rtcpCand.setGeneration(generation); + rtcpCand.setID(getNextID()); + rtcpCand.setType(CandidateType.host); + + DatagramSocket controlSocket = connector.getControlSocket(); + + rtcpCand.setIP(controlSocket.getLocalAddress().getHostAddress()); + rtcpCand.setPort(controlSocket.getLocalPort()); + + ourTransport.addCandidate(rtcpCand); + + return ourTransport; + } + + /** + * {@inheritDoc} + */ + protected PacketExtension createTransportPacketExtension() + { + return new RawUdpTransportPacketExtension(); + } + + /** + * Implements {@link TransportManagerJabberImpl#getStreamTarget(MediaType)}. + * Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of + * the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>. + * + * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which + * is to have its <tt>target</tt> set to the returned + * <tt>MediaStreamTarget</tt> + * @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> + * of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt> + * @see TransportManagerJabberImpl#getStreamTarget(MediaType) + */ + @Override + public MediaStreamTarget getStreamTarget(MediaType mediaType) + { + ColibriConferenceIQ.Channel channel + = getColibriChannel(mediaType, true /* local */); + MediaStreamTarget streamTarget = null; + + if (channel == null) + { + String media = mediaType.toString(); + + for (Iterable<ContentPacketExtension> remote : remotes) + { + for (ContentPacketExtension content : remote) + { + RtpDescriptionPacketExtension rtpDescription + = content.getFirstChildOfType( + RtpDescriptionPacketExtension.class); + + if (media.equals(rtpDescription.getMedia())) + { + streamTarget + = JingleUtils.extractDefaultTarget(content); + break; + } + } + } + } + else + { + IceUdpTransportPacketExtension transport = channel.getTransport(); + + if (transport != null) + streamTarget = JingleUtils.extractDefaultTarget(transport); + if (streamTarget == null) + { + /* + * For the purposes of compatibility with legacy Jitsi + * Videobridge, support the channel attributes host, rtpPort and + * rtcpPort. + */ + @SuppressWarnings("deprecation") + String host = channel.getHost(); + + if (host != null) + { + @SuppressWarnings("deprecation") + int rtpPort = channel.getRTPPort(); + @SuppressWarnings("deprecation") + int rtcpPort = channel.getRTCPPort(); + + streamTarget + = new MediaStreamTarget( + new InetSocketAddress(host, rtpPort), + new InetSocketAddress(host, rtcpPort)); + } + } + } + return streamTarget; + } + + /** + * Implements {@link TransportManagerJabberImpl#getXmlNamespace()}. Gets the + * XML namespace of the Jingle transport implemented by this + * <tt>TransportManagerJabberImpl</tt>. + * + * @return the XML namespace of the Jingle transport implemented by this + * <tt>TransportManagerJabberImpl</tt> + * @see TransportManagerJabberImpl#getXmlNamespace() + */ + @Override + public String getXmlNamespace() + { + return ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0; + } + + /** + * Removes a content with a specific name from the transport-related part of + * the session represented by this <tt>TransportManagerJabberImpl</tt> which + * may have been reported through previous calls to the + * <tt>startCandidateHarvest</tt> and + * <tt>startConnectivityEstablishment</tt> methods. + * + * @param name the name of the content to be removed from the + * transport-related part of the session represented by this + * <tt>TransportManagerJabberImpl</tt> + * @see TransportManagerJabberImpl#removeContent(String) + */ + @Override + public void removeContent(String name) + { + if (local != null) + removeContent(local, name); + + removeRemoteContent(name); + } + + /** + * Removes a content with a specific name from the remote counterpart of the + * negotiation between the local and the remote peers. + * + * @param name the name of the content to be removed from the remote + * counterpart of the negotiation between the local and the remote peers + */ + private void removeRemoteContent(String name) + { + for (Iterator<Iterable<ContentPacketExtension>> remoteIter + = remotes.iterator(); + remoteIter.hasNext();) + { + Iterable<ContentPacketExtension> remote = remoteIter.next(); + + /* + * Once the remote content is removed, make sure that we are not + * retaining sets which do not have any contents. + */ + if ((removeContent(remote, name) != null) + && !remote.iterator().hasNext()) + { + remoteIter.remove(); + } + } + } + + /** + * {@inheritDoc} + */ + protected PacketExtension startCandidateHarvest( + ContentPacketExtension theirContent, + ContentPacketExtension ourContent, + TransportInfoSender transportInfoSender, + String media) + throws OperationFailedException + { + return createTransportForStartCandidateHarvest(media); + } + + /** + * Starts transport candidate harvest. This method should complete rapidly + * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests + * are necessary, they should be executed in a separate thread. Candidate + * harvest would then need to be concluded in the + * {@link #wrapupCandidateHarvest()} method which would be called once we + * absolutely need the candidates. + * + * @param theirOffer a media description offer that we've received from the + * remote party and that we should use in case we need to know what + * transports our peer is using. + * @param ourAnswer the content descriptions that we should be adding our + * transport lists to (although not necessarily in this very instance). + * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by + * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt> + * <tt>JingleIQ</tt>s from the local peer to the remote peer if this + * <tt>TransportManagerJabberImpl</tt> wishes to utilize + * <tt>transport-info</tt>. Local candidate addresses sent by this + * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are + * expected to not be included in the result of + * {@link #wrapupCandidateHarvest()}. + * + * @throws OperationFailedException if we fail to allocate a port number. + * @see TransportManagerJabberImpl#startCandidateHarvest(List, List, + * TransportInfoSender) + */ + @Override + public void startCandidateHarvest( + List<ContentPacketExtension> theirOffer, + List<ContentPacketExtension> ourAnswer, + TransportInfoSender transportInfoSender) + throws OperationFailedException + { + this.local = ourAnswer; + + super.startCandidateHarvest(theirOffer, ourAnswer, transportInfoSender); + } + + /** + * Overrides the super implementation in order to remember the remote + * counterpart of the negotiation between the local and the remote peer for + * subsequent calls to {@link #getStreamTarget(MediaType)}. + * + * @param remote the collection of <tt>ContentPacketExtension</tt>s which + * represents the remote counterpart of the negotiation between the local + * and the remote peer + * @return <tt>true</tt> because <tt>RawUdpTransportManager</tt> does not + * perform connectivity checks + * @see TransportManagerJabberImpl#startConnectivityEstablishment(Iterable) + */ + @Override + public boolean startConnectivityEstablishment( + Iterable<ContentPacketExtension> remote) + { + if ((remote != null) && !remotes.contains(remote)) + { + /* + * The state of the session in Jingle is maintained by each peer and + * is modified by content-add and content-remove. The remotes field + * of this RawUdpTransportManager represents the state of the + * session with respect to the remote peer. When the remote peer + * tells us about a specific set of contents, make sure that it is + * the only record we will have with respect to the specified set of + * contents. + */ + for (ContentPacketExtension content : remote) + removeRemoteContent(content.getName()); + + remotes.add(remote); + } + + return super.startConnectivityEstablishment(remote); + } + + /** + * Simply returns the list of local candidates that we gathered during the + * harvest. This is a raw UDP transport manager so there's no real wrapping + * up to do. + * + * @return the list of local candidates that we gathered during the harvest + * @see TransportManagerJabberImpl#wrapupCandidateHarvest() + */ + @Override + public List<ContentPacketExtension> wrapupCandidateHarvest() + { + return local; + } + + /** + * Returns the extended type of the candidate selected if this transport + * manager is using ICE. + * + * @param streamName The stream name (AUDIO, VIDEO); + * + * @return The extended type of the candidate selected if this transport + * manager is using ICE. Otherwise, returns null. + */ + @Override + public String getICECandidateExtendedType(String streamName) + { + return null; + } + + /** + * Returns the current state of ICE processing. + * + * @return the current state of ICE processing. + */ + @Override + public String getICEState() + { + return null; + } + + /** + * Returns the ICE local host address. + * + * @param streamName The stream name (AUDIO, VIDEO); + * + * @return the ICE local host address if this transport + * manager is using ICE. Otherwise, returns null. + */ + @Override + public InetSocketAddress getICELocalHostAddress(String streamName) + { + return null; + } + + /** + * Returns the ICE remote host address. + * + * @param streamName The stream name (AUDIO, VIDEO); + * + * @return the ICE remote host address if this transport + * manager is using ICE. Otherwise, returns null. + */ + @Override + public InetSocketAddress getICERemoteHostAddress(String streamName) + { + return null; + } + + /** + * Returns the ICE local reflexive address (server or peer reflexive). + * + * @param streamName The stream name (AUDIO, VIDEO); + * + * @return the ICE local reflexive address. May be null if this transport + * manager is not using ICE or if there is no reflexive address for the + * local candidate used. + */ + @Override + public InetSocketAddress getICELocalReflexiveAddress(String streamName) + { + return null; + } + + /** + * Returns the ICE remote reflexive address (server or peer reflexive). + * + * @param streamName The stream name (AUDIO, VIDEO); + * + * @return the ICE remote reflexive address. May be null if this transport + * manager is not using ICE or if there is no reflexive address for the + * remote candidate used. + */ + @Override + public InetSocketAddress getICERemoteReflexiveAddress(String streamName) + { + return null; + } + + /** + * Returns the ICE local relayed address (server or peer relayed). + * + * @param streamName The stream name (AUDIO, VIDEO); + * + * @return the ICE local relayed address. May be null if this transport + * manager is not using ICE or if there is no relayed address for the + * local candidate used. + */ + @Override + public InetSocketAddress getICELocalRelayedAddress(String streamName) + { + return null; + } + + /** + * Returns the ICE remote relayed address (server or peer relayed). + * + * @param streamName The stream name (AUDIO, VIDEO); + * + * @return the ICE remote relayed address. May be null if this transport + * manager is not using ICE or if there is no relayed address for the + * remote candidate used. + */ + @Override + public InetSocketAddress getICERemoteRelayedAddress(String streamName) + { + return null; + } + + /** + * Returns the total harvesting time (in ms) for all harvesters. + * + * @return The total harvesting time (in ms) for all the harvesters. 0 if + * the ICE agent is null, or if the agent has nevers harvested. + */ + @Override + public long getTotalHarvestingTime() + { + return 0; + } + + /** + * Returns the harvesting time (in ms) for the harvester given in parameter. + * + * @param harvesterName The class name if the harvester. + * + * @return The harvesting time (in ms) for the harvester given in parameter. + * 0 if this harvester does not exists, if the ICE agent is null, or if the + * agent has never harvested with this harvester. + */ + @Override + public long getHarvestingTime(String harvesterName) + { + return 0; + } + + /** + * Returns the number of harvesting for this agent. + * + * @return The number of harvesting for this agent. + */ + @Override + public int getNbHarvesting() + { + return 0; + } + + /** + * Returns the number of harvesting time for the harvester given in + * parameter. + * + * @param harvesterName The class name if the harvester. + * + * @return The number of harvesting time for the harvester given in + * parameter. + */ + @Override + public int getNbHarvesting(String harvesterName) + { + return 0; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ScServiceDiscoveryManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/ScServiceDiscoveryManager.java index 9fdbea5..de2ce3e 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ScServiceDiscoveryManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ScServiceDiscoveryManager.java @@ -27,6 +27,7 @@ import net.java.sip.communicator.util.*; import org.jivesoftware.smack.*; import org.jivesoftware.smack.filter.*; import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.util.*; import org.jivesoftware.smackx.*; import org.jivesoftware.smackx.packet.*; @@ -79,9 +80,9 @@ public class ScServiceDiscoveryManager private final ProtocolProviderService parentProvider; /** - * The {@link XMPPConnection} that this manager is responsible for. + * The {@link Connection} that this manager is responsible for. */ - private final XMPPConnection connection; + private final Connection connection; /** * A local copy that we keep in sync with {@link ServiceDiscoveryManager}'s @@ -129,7 +130,7 @@ public class ScServiceDiscoveryManager */ public ScServiceDiscoveryManager( ProtocolProviderService parentProvider, - XMPPConnection connection, + Connection connection, String[] featuresToRemove, String[] featuresToAdd, boolean cacheNonCaps) @@ -791,7 +792,11 @@ public class ScServiceDiscoveryManager // fire event if(fireEvent && capabilitiesOpSet != null) { - capabilitiesOpSet.fireContactCapabilitiesChanged(entityID); + capabilitiesOpSet.fireContactCapabilitiesChanged( + entityID, + capsManager.getFullJidsByBareJid( + StringUtils.parseBareAddress(entityID)) + ); } } catch(XMPPException ex) diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/SmackV3InteroperabilityLayer.java b/src/net/java/sip/communicator/impl/protocol/jabber/SmackV3InteroperabilityLayer.java new file mode 100644 index 0000000..d5af26f --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/SmackV3InteroperabilityLayer.java @@ -0,0 +1,91 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber; + +import net.java.sip.communicator.service.protocol.jabber.*; +import org.jivesoftware.smack.provider.*; + +/** + * Smack v3 interoperation layer + * + * @author Maksym Kulish + */ +public class SmackV3InteroperabilityLayer + extends AbstractSmackInteroperabilityLayer +{ + + /** + * A SmackV3 ProviderManager instance + */ + private ProviderManager providerManager = ProviderManager.getInstance(); + + /** + * A default constructor + */ + public SmackV3InteroperabilityLayer() {} + + /** + * Add <tt>PacketExtensionProvider</tt> to the list of known + * providers + * + * @param elementName The element name where the matching is happening + * @param namespace The XML namespace used in that element + * @param provider <tt>PacketExtensionProvider</tt> implementation to be + * used + */ + @Override + public void addExtensionProvider( + String elementName, String namespace, Object provider) + { + providerManager.addExtensionProvider(elementName, namespace, provider); + } + + /** + * Add <tt>IQProvider</tt> to the list of known + * providers + * + * @param elementName The element name where the matching is happening + * @param namespace The XML namespace used in that element + * @param provider <tt>IQProvider</tt> implementation to be + * used + */ + @Override + public void addIQProvider( + String elementName, String namespace, Object provider) + { + providerManager.addIQProvider(elementName, namespace, provider); + } + + /** + * Get the <tt>PacketExtensionProvider</tt> for given element name and XML + * namespace + * + * @param elementName The element name where the matching is happening + * @param namespace The XML namespace used in that element + * @return <tt>PacketExtensionProvider</tt> implementation to be + * used + */ + @Override + public PacketExtensionProvider getExtensionProvider( + String elementName, String namespace) + { + return (PacketExtensionProvider)providerManager + .getExtensionProvider(elementName, namespace); + } + +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java index f7f47c6..41e8c05 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,963 +15,963 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.net.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
-import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.media.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.service.neomedia.*;
-import org.jivesoftware.smack.packet.*;
-
-/**
- * <tt>TransportManager</tt>s gather local candidates for incoming and outgoing
- * calls. Their work starts by calling a start method which, using the remote
- * peer's session description, would start the harvest. Calling a second wrapup
- * method would deliver the candidate harvest, possibly after blocking if it has
- * not yet completed.
- *
- * @author Emil Ivov
- * @author Lyubomir Marinov
- */
-public abstract class TransportManagerJabberImpl
- extends TransportManager<CallPeerJabberImpl>
-{
- /**
- * The <tt>Logger</tt> used by the <tt>TransportManagerJabberImpl</tt> class
- * and its instances to print debug messages.
- */
- private static final Logger logger
- = Logger.getLogger(TransportManagerJabberImpl.class);
-
- /**
- * The ID that we will be assigning to our next candidate. We use
- * <tt>int</tt>s for interoperability reasons (Emil: I believe that GTalk
- * uses <tt>int</tt>s. If that turns out not to be the case we can stop
- * using <tt>int</tt>s here if that's an issue).
- */
- private static int nextID = 1;
-
- /**
- * The information pertaining to the Jisti Videobridge conference which the
- * local peer represented by this instance is a focus of. It gives a view of
- * the whole Jitsi Videobridge conference managed by the associated
- * <tt>CallJabberImpl</tt> which provides information specific to this
- * <tt>TransportManager</tt> only.
- */
- private ColibriConferenceIQ colibri;
-
- /**
- * The generation of the candidates we are currently generating
- */
- private int currentGeneration = 0;
-
- /**
- * The indicator which determines whether this <tt>TransportManager</tt>
- * instance is responsible to establish the connectivity with the associated
- * Jitsi Videobridge (in case it is being employed at all).
- */
- boolean isEstablishingConnectivityWithJitsiVideobridge = false;
-
- /**
- * The indicator which determines whether this <tt>TransportManager</tt>
- * instance is yet to start establishing the connectivity with the
- * associated Jitsi Videobridge (in case it is being employed at all).
- */
- boolean startConnectivityEstablishmentWithJitsiVideobridge = false;
-
- /**
- * Creates a new instance of this transport manager, binding it to the
- * specified peer.
- *
- * @param callPeer the {@link CallPeer} whose traffic we will be taking
- * care of.
- */
- protected TransportManagerJabberImpl(CallPeerJabberImpl callPeer)
- {
- super(callPeer);
- }
-
- /**
- * Returns the <tt>InetAddress</tt> that is most likely to be to be used
- * as a next hop when contacting the specified <tt>destination</tt>. This is
- * an utility method that is used whenever we have to choose one of our
- * local addresses to put in the Via, Contact or (in the case of no
- * registrar accounts) From headers.
- *
- * @param peer the CallPeer that we would contact.
- *
- * @return the <tt>InetAddress</tt> that is most likely to be to be used
- * as a next hop when contacting the specified <tt>destination</tt>.
- *
- * @throws IllegalArgumentException if <tt>destination</tt> is not a valid
- * host/IP/FQDN
- */
- @Override
- protected InetAddress getIntendedDestination(CallPeerJabberImpl peer)
- {
- return peer.getProtocolProvider().getNextHop();
- }
-
- /**
- * Returns the ID that we will be assigning to the next candidate we create.
- *
- * @return the next ID to use with a candidate.
- */
- protected String getNextID()
- {
- int nextID;
-
- synchronized (TransportManagerJabberImpl.class)
- {
- nextID = TransportManagerJabberImpl.nextID++;
- }
- return Integer.toString(nextID);
- }
-
- /**
- * Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of
- * the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
- * is to have its <tt>target</tt> set to the returned
- * <tt>MediaStreamTarget</tt>
- * @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt>
- * of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
- */
- public abstract MediaStreamTarget getStreamTarget(MediaType mediaType);
-
- /**
- * Gets the XML namespace of the Jingle transport implemented by this
- * <tt>TransportManagerJabberImpl</tt>.
- *
- * @return the XML namespace of the Jingle transport implemented by this
- * <tt>TransportManagerJabberImpl</tt>
- */
- public abstract String getXmlNamespace();
-
- /**
- * Returns the generation that our current candidates belong to.
- *
- * @return the generation that we should assign to candidates that we are
- * currently advertising.
- */
- protected int getCurrentGeneration()
- {
- return currentGeneration;
- }
-
- /**
- * Increments the generation that we are assigning candidates.
- */
- protected void incrementGeneration()
- {
- currentGeneration++;
- }
-
- /**
- * Sends transport-related information received from the remote peer to the
- * associated Jiitsi Videobridge in order to update the (remote)
- * <tt>ColibriConferenceIQ.Channel</tt> associated with this
- * <tt>TransportManager</tt> instance.
- *
- * @param map a <tt>Map</tt> of media-IceUdpTransportPacketExtension pairs
- * which represents the transport-related information which has been
- * received from the remote peer and which is to be sent to the associated
- * Jitsi Videobridge
- */
- protected void sendTransportInfoToJitsiVideobridge(
- Map<String,IceUdpTransportPacketExtension> map)
- {
- CallPeerJabberImpl peer = getCallPeer();
- boolean initiator = !peer.isInitiator();
- ColibriConferenceIQ conferenceRequest = null;
-
- for (Map.Entry<String,IceUdpTransportPacketExtension> e
- : map.entrySet())
- {
- String media = e.getKey();
- MediaType mediaType = MediaType.parseString(media);
- ColibriConferenceIQ.Channel channel
- = getColibriChannel(mediaType, false /* remote */);
-
- if (channel != null)
- {
- IceUdpTransportPacketExtension transport;
-
- try
- {
- transport = cloneTransportAndCandidates(e.getValue());
- }
- catch (OperationFailedException ofe)
- {
- transport = null;
- }
- if (transport == null)
- continue;
-
- ColibriConferenceIQ.Channel channelRequest
- = new ColibriConferenceIQ.Channel();
-
- channelRequest.setID(channel.getID());
- channelRequest.setInitiator(initiator);
- channelRequest.setTransport(transport);
-
- if (conferenceRequest == null)
- {
- if (colibri == null)
- break;
- else
- {
- String id = colibri.getID();
-
- if ((id == null) || (id.length() == 0))
- break;
- else
- {
- conferenceRequest = new ColibriConferenceIQ();
- conferenceRequest.setID(id);
- conferenceRequest.setTo(colibri.getFrom());
- conferenceRequest.setType(IQ.Type.SET);
- }
- }
- }
- conferenceRequest.getOrCreateContent(media).addChannel(
- channelRequest);
- }
- }
- if (conferenceRequest != null)
- {
- peer.getProtocolProvider().getConnection().sendPacket(
- conferenceRequest);
- }
- }
-
- /**
- * Starts transport candidate harvest for a specific
- * <tt>ContentPacketExtension</tt> that we are going to offer or answer
- * with.
- *
- * @param theirContent the <tt>ContentPacketExtension</tt> offered by the
- * remote peer to which we are going to answer with <tt>ourContent</tt> or
- * <tt>null</tt> if <tt>ourContent</tt> will be an offer to the remote peer
- * @param ourContent the <tt>ContentPacketExtension</tt> for which transport
- * candidate harvest is to be started
- * @param transportInfoSender a <tt>TransportInfoSender</tt> if the
- * harvested transport candidates are to be sent in a
- * <tt>transport-info</tt> rather than in <tt>ourContent</tt>; otherwise,
- * <tt>null</tt>
- * @param media the media of the <tt>RtpDescriptionPacketExtension</tt>
- * child of <tt>ourContent</tt>
- * @return a <tt>PacketExtension</tt> to be added as a child to
- * <tt>ourContent</tt>; otherwise, <tt>null</tt>
- * @throws OperationFailedException if anything goes wrong while starting
- * transport candidate harvest for the specified <tt>ourContent</tt>
- */
- protected abstract PacketExtension startCandidateHarvest(
- ContentPacketExtension theirContent,
- ContentPacketExtension ourContent,
- TransportInfoSender transportInfoSender,
- String media)
- throws OperationFailedException;
-
- /**
- * Starts transport candidate harvest. This method should complete rapidly
- * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
- * are necessary, they should be executed in a separate thread. Candidate
- * harvest would then need to be concluded in the
- * {@link #wrapupCandidateHarvest()} method which would be called once we
- * absolutely need the candidates.
- *
- * @param theirOffer a media description offer that we've received from the
- * remote party and that we should use in case we need to know what
- * transports our peer is using.
- * @param ourAnswer the content descriptions that we should be adding our
- * transport lists to (although not necessarily in this very instance).
- * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
- * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
- * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
- * <tt>TransportManagerJabberImpl</tt> wishes to utilize
- * <tt>transport-info</tt>. Local candidate addresses sent by this
- * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
- * expected to not be included in the result of
- * {@link #wrapupCandidateHarvest()}.
- *
- * @throws OperationFailedException if we fail to allocate a port number.
- */
- public void startCandidateHarvest(
- List<ContentPacketExtension> theirOffer,
- List<ContentPacketExtension> ourAnswer,
- TransportInfoSender transportInfoSender)
- throws OperationFailedException
- {
- CallPeerJabberImpl peer = getCallPeer();
- CallJabberImpl call = peer.getCall();
- boolean isJitsiVideobridge = call.getConference().isJitsiVideobridge();
- List<ContentPacketExtension> cpes
- = (theirOffer == null) ? ourAnswer : theirOffer;
-
- /*
- * If Jitsi Videobridge is to be used, determine which channels are to
- * be allocated and attempt to allocate them now.
- */
- if (isJitsiVideobridge)
- {
- Map<ContentPacketExtension,ContentPacketExtension> contentMap
- = new LinkedHashMap
- <ContentPacketExtension,ContentPacketExtension>();
-
- for (ContentPacketExtension cpe : cpes)
- {
- MediaType mediaType = JingleUtils.getMediaType(cpe);
-
- /*
- * The existence of a content for the mediaType and regardless
- * of the existence of channels in it signals that a channel
- * allocation request has already been sent for that mediaType.
- */
- if ((colibri == null)
- || (colibri.getContent(mediaType.toString()) == null))
- {
- ContentPacketExtension local, remote;
-
- if (cpes == ourAnswer)
- {
- local = cpe;
- remote
- = (theirOffer == null)
- ? null
- : findContentByName(theirOffer, cpe.getName());
- }
- else
- {
- local = findContentByName(ourAnswer, cpe.getName());
- remote = cpe;
- }
- contentMap.put(local, remote);
- }
- }
- if (!contentMap.isEmpty())
- {
- /*
- * We are about to request the channel allocations for the media
- * types found in contentMap. Regardless of the response, we do
- * not want to repeat these requests.
- */
- if (colibri == null)
- colibri = new ColibriConferenceIQ();
- for (Map.Entry<ContentPacketExtension,ContentPacketExtension> e
- : contentMap.entrySet())
- {
- ContentPacketExtension cpe = e.getValue();
-
- if (cpe == null)
- cpe = e.getKey();
-
- colibri.getOrCreateContent(
- JingleUtils.getMediaType(cpe).toString());
- }
-
- ColibriConferenceIQ conferenceResult
- = call.createColibriChannels(peer, contentMap);
-
- if (conferenceResult != null)
- {
- String videobridgeID = colibri.getID();
- String conferenceResultID = conferenceResult.getID();
-
- if (videobridgeID == null)
- colibri.setID(conferenceResultID);
- else if (!videobridgeID.equals(conferenceResultID))
- throw new IllegalStateException("conference.id");
-
- String videobridgeFrom = conferenceResult.getFrom();
-
- if ((videobridgeFrom != null)
- && (videobridgeFrom.length() != 0))
- {
- colibri.setFrom(videobridgeFrom);
- }
-
- for (ColibriConferenceIQ.Content contentResult
- : conferenceResult.getContents())
- {
- ColibriConferenceIQ.Content content
- = colibri.getOrCreateContent(
- contentResult.getName());
-
- for (ColibriConferenceIQ.Channel channelResult
- : contentResult.getChannels())
- {
- if (content.getChannel(channelResult.getID())
- == null)
- {
- content.addChannel(channelResult);
- }
- }
- }
- }
- else
- {
- /*
- * The call fails if the createColibriChannels method fails
- * which may happen if the conference packet times out or it
- * can't be built.
- */
- ProtocolProviderServiceJabberImpl
- .throwOperationFailedException(
- "Failed to allocate colibri channel.",
- OperationFailedException.GENERAL_ERROR,
- null,
- logger);
- }
- }
- }
-
- for (ContentPacketExtension cpe : cpes)
- {
- String contentName = cpe.getName();
- ContentPacketExtension ourContent
- = findContentByName(ourAnswer, contentName);
-
- //it might be that we decided not to reply to this content
- if (ourContent != null)
- {
- ContentPacketExtension theirContent
- = (theirOffer == null)
- ? null
- : findContentByName(theirOffer, contentName);
- RtpDescriptionPacketExtension rtpDesc
- = ourContent.getFirstChildOfType(
- RtpDescriptionPacketExtension.class);
- String media = rtpDesc.getMedia();
- PacketExtension pe
- = startCandidateHarvest(
- theirContent,
- ourContent,
- transportInfoSender,
- media);
-
- if (pe != null)
- ourContent.addChildExtension(pe);
- }
- }
- }
-
- /**
- * Starts transport candidate harvest. This method should complete rapidly
- * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
- * are necessary, they should be executed in a separate thread. Candidate
- * harvest would then need to be concluded in the
- * {@link #wrapupCandidateHarvest()} method which would be called once we
- * absolutely need the candidates.
- *
- * @param ourOffer the content descriptions that we should be adding our
- * transport lists to (although not necessarily in this very instance).
- * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
- * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
- * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
- * <tt>TransportManagerJabberImpl</tt> wishes to utilize
- * <tt>transport-info</tt>. Local candidate addresses sent by this
- * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
- * expected to not be included in the result of
- * {@link #wrapupCandidateHarvest()}.
- * @throws OperationFailedException if we fail to allocate a port number.
- */
- public void startCandidateHarvest(
- List<ContentPacketExtension> ourOffer,
- TransportInfoSender transportInfoSender)
- throws OperationFailedException
- {
- startCandidateHarvest(
- /* theirOffer */ null,
- ourOffer,
- transportInfoSender);
- }
-
- /**
- * Notifies the transport manager that it should conclude candidate
- * harvesting as soon as possible and return the lists of candidates
- * gathered so far.
- *
- * @return the content list that we received earlier (possibly cloned into
- * a new instance) and that we have updated with transport lists.
- */
- public abstract List<ContentPacketExtension> wrapupCandidateHarvest();
-
- /**
- * Looks through the <tt>cpExtList</tt> and returns the {@link
- * ContentPacketExtension} with the specified name.
- *
- * @param cpExtList the list that we will be searching for a specific
- * content.
- * @param name the name of the content element we are looking for.
- * @return the {@link ContentPacketExtension} with the specified name or
- * <tt>null</tt> if no such content element exists.
- */
- public static ContentPacketExtension findContentByName(
- Iterable<ContentPacketExtension> cpExtList,
- String name)
- {
- for(ContentPacketExtension cpExt : cpExtList)
- {
- if(cpExt.getName().equals(name))
- return cpExt;
- }
- return null;
- }
-
- /**
- * Starts the connectivity establishment of this
- * <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between
- * the local and the remote peers given the remote counterpart of the
- * negotiation between them.
- *
- * @param remote the collection of <tt>ContentPacketExtension</tt>s which
- * represents the remote counterpart of the negotiation between the local
- * and the remote peer
- * @return <tt>true</tt> if connectivity establishment has been started in
- * response to the call; otherwise, <tt>false</tt>.
- * <tt>TransportManagerJabberImpl</tt> implementations which do not perform
- * connectivity checks (e.g. raw UDP) should return <tt>true</tt>. The
- * default implementation does not perform connectivity checks and always
- * returns <tt>true</tt>.
- */
- public boolean startConnectivityEstablishment(
- Iterable<ContentPacketExtension> remote)
- {
- return true;
- }
-
- /**
- * Starts the connectivity establishment of this
- * <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between
- * the local and the remote peers given the remote counterpart of the
- * negotiation between them.
- *
- * @param remote a <tt>Map</tt> of
- * media-<tt>IceUdpTransportPacketExtension</tt> pairs which represents the
- * remote counterpart of the negotiation between the local and the remote
- * peers
- * @return <tt>true</tt> if connectivity establishment has been started in
- * response to the call; otherwise, <tt>false</tt>.
- * <tt>TransportManagerJabberImpl</tt> implementations which do not perform
- * connectivity checks (e.g. raw UDP) should return <tt>true</tt>. The
- * default implementation does not perform connectivity checks and always
- * returns <tt>true</tt>.
- */
- protected boolean startConnectivityEstablishment(
- Map<String,IceUdpTransportPacketExtension> remote)
- {
- return true;
- }
-
- /**
- * Notifies this <tt>TransportManagerJabberImpl</tt> that it should conclude
- * any started connectivity establishment.
- *
- * @throws OperationFailedException if anything goes wrong with connectivity
- * establishment (i.e. ICE failed, ...)
- */
- public void wrapupConnectivityEstablishment()
- throws OperationFailedException
- {
- }
-
- /**
- * Removes a content with a specific name from the transport-related part of
- * the session represented by this <tt>TransportManagerJabberImpl</tt> which
- * may have been reported through previous calls to the
- * <tt>startCandidateHarvest</tt> and
- * <tt>startConnectivityEstablishment</tt> methods.
- * <p>
- * <b>Note</b>: Because <tt>TransportManager</tt> deals with
- * <tt>MediaType</tt>s, not content names and
- * <tt>TransportManagerJabberImpl</tt> does not implement translating from
- * content name to <tt>MediaType</tt>, implementers are expected to call
- * {@link TransportManager#closeStreamConnector(MediaType)}.
- * </p>
- *
- * @param name the name of the content to be removed from the
- * transport-related part of the session represented by this
- * <tt>TransportManagerJabberImpl</tt>
- */
- public abstract void removeContent(String name);
-
- /**
- * Removes a content with a specific name from a specific collection of
- * contents and closes any associated <tt>StreamConnector</tt>.
- *
- * @param contents the collection of contents to remove the content with the
- * specified name from
- * @param name the name of the content to remove
- * @return the removed <tt>ContentPacketExtension</tt> if any; otherwise,
- * <tt>null</tt>
- */
- protected ContentPacketExtension removeContent(
- Iterable<ContentPacketExtension> contents,
- String name)
- {
- for (Iterator<ContentPacketExtension> contentIter = contents.iterator();
- contentIter.hasNext();)
- {
- ContentPacketExtension content = contentIter.next();
-
- if (name.equals(content.getName()))
- {
- contentIter.remove();
-
- // closeStreamConnector
- MediaType mediaType = JingleUtils.getMediaType(content);
- if (mediaType != null)
- {
- closeStreamConnector(mediaType);
- }
-
- return content;
- }
- }
- return null;
- }
-
- /**
- * Clones a specific <tt>IceUdpTransportPacketExtension</tt> and its
- * candidates.
- *
- * @param src the <tt>IceUdpTransportPacketExtension</tt> to be cloned
- * @return a new <tt>IceUdpTransportPacketExtension</tt> instance which has
- * the same run-time type, attributes, namespace, text and candidates as the
- * specified <tt>src</tt>
- * @throws OperationFailedException if an error occurs during the cloing of
- * the specified <tt>src</tt> and its candidates
- */
- static IceUdpTransportPacketExtension cloneTransportAndCandidates(
- IceUdpTransportPacketExtension src)
- throws OperationFailedException
- {
- try
- {
- return IceUdpTransportPacketExtension
- .cloneTransportAndCandidates(src);
- }
- catch (Exception e)
- {
- ProtocolProviderServiceJabberImpl
- .throwOperationFailedException(
- "Failed to close transport and candidates.",
- OperationFailedException.GENERAL_ERROR,
- e,
- logger);
-
- }
- return null;
- }
-
- /**
- * Releases the resources acquired by this <tt>TransportManager</tt> and
- * prepares it for garbage collection.
- */
- public void close()
- {
- for (MediaType mediaType : MediaType.values())
- closeStreamConnector(mediaType);
- }
-
- /**
- * Closes a specific <tt>StreamConnector</tt> associated with a specific
- * <tt>MediaType</tt>. If this <tt>TransportManager</tt> has a reference to
- * the specified <tt>streamConnector</tt>, it remains.
- * Also expires the <tt>ColibriConferenceIQ.Channel</tt> associated with
- * the closed <tt>StreamConnector</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> associated with the specified
- * <tt>streamConnector</tt>
- * @param streamConnector the <tt>StreamConnector</tt> to be closed
- */
- @Override
- protected void closeStreamConnector(
- MediaType mediaType,
- StreamConnector streamConnector)
- {
- try
- {
- boolean superCloseStreamConnector = true;
-
- if (streamConnector instanceof ColibriStreamConnector)
- {
- CallPeerJabberImpl peer = getCallPeer();
-
- if (peer != null)
- {
- CallJabberImpl call = peer.getCall();
-
- if (call != null)
- {
- superCloseStreamConnector = false;
- call.closeColibriStreamConnector(
- peer,
- mediaType,
- (ColibriStreamConnector) streamConnector);
- }
- }
- }
- if (superCloseStreamConnector)
- super.closeStreamConnector(mediaType, streamConnector);
- }
- finally
- {
- /*
- * Expire the ColibriConferenceIQ.Channel associated with the closed
- * StreamConnector.
- */
- if (colibri != null)
- {
- ColibriConferenceIQ.Content content
- = colibri.getContent(mediaType.toString());
-
- if (content != null)
- {
- List<ColibriConferenceIQ.Channel> channels
- = content.getChannels();
-
- if (channels.size() == 2)
- {
- ColibriConferenceIQ requestConferenceIQ
- = new ColibriConferenceIQ();
-
- requestConferenceIQ.setID(colibri.getID());
-
- ColibriConferenceIQ.Content requestContent
- = requestConferenceIQ.getOrCreateContent(
- content.getName());
-
- requestContent.addChannel(channels.get(1 /* remote */));
-
- /*
- * Regardless of whether the request to expire the
- * Channel associated with mediaType succeeds, consider
- * the Channel in question expired. Since
- * RawUdpTransportManager allocates a single channel per
- * MediaType, consider the whole Content expired.
- */
- colibri.removeContent(content);
-
- CallPeerJabberImpl peer = getCallPeer();
-
- if (peer != null)
- {
- CallJabberImpl call = peer.getCall();
-
- if (call != null)
- {
- call.expireColibriChannels(
- peer,
- requestConferenceIQ);
- }
- }
- }
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * Adds support for telephony conferences utilizing the Jitsi Videobridge
- * server-side technology.
- *
- * @see #doCreateStreamConnector(MediaType)
- */
- @Override
- protected StreamConnector createStreamConnector(final MediaType mediaType)
- throws OperationFailedException
- {
- ColibriConferenceIQ.Channel channel
- = getColibriChannel(mediaType, true /* local */);
-
- if (channel != null)
- {
- CallPeerJabberImpl peer = getCallPeer();
- CallJabberImpl call = peer.getCall();
- StreamConnector streamConnector
- = call.createColibriStreamConnector(
- peer,
- mediaType,
- channel,
- new StreamConnectorFactory()
- {
- public StreamConnector createStreamConnector()
- {
- try
- {
- return doCreateStreamConnector(mediaType);
- }
- catch (OperationFailedException ofe)
- {
- return null;
- }
- }
- });
-
- if (streamConnector != null)
- return streamConnector;
- }
-
- return doCreateStreamConnector(mediaType);
- }
-
- protected abstract PacketExtension createTransport(String media)
- throws OperationFailedException;
-
- protected PacketExtension createTransportForStartCandidateHarvest(
- String media)
- throws OperationFailedException
- {
- PacketExtension pe = null;
-
- if (getCallPeer().isJitsiVideobridge())
- {
- MediaType mediaType = MediaType.parseString(media);
- ColibriConferenceIQ.Channel channel
- = getColibriChannel(mediaType, false /* remote */);
-
- if (channel != null)
- pe = cloneTransportAndCandidates(channel.getTransport());
- }
- else
- pe = createTransport(media);
- return pe;
- }
-
- /**
- * Initializes a new <tt>PacketExtension</tt> instance appropriate to the
- * type of Jingle transport represented by this <tt>TransportManager</tt>.
- * The new instance is not initialized with any attributes or child
- * extensions.
- *
- * @return a new <tt>PacketExtension</tt> instance appropriate to the type
- * of Jingle transport represented by this <tt>TransportManager</tt>
- */
- protected abstract PacketExtension createTransportPacketExtension();
-
- /**
- * Creates a media <tt>StreamConnector</tt> for a stream of a specific
- * <tt>MediaType</tt>. The minimum and maximum of the media port boundaries
- * are taken into account.
- *
- * @param mediaType the <tt>MediaType</tt> of the stream for which a
- * <tt>StreamConnector</tt> is to be created
- * @return a <tt>StreamConnector</tt> for the stream of the specified
- * <tt>mediaType</tt>
- * @throws OperationFailedException if the binding of the sockets fails
- */
- protected StreamConnector doCreateStreamConnector(MediaType mediaType)
- throws OperationFailedException
- {
- return super.createStreamConnector(mediaType);
- }
-
- /**
- * Finds a <tt>TransportManagerJabberImpl</tt> participating in a telephony
- * conference utilizing the Jitsi Videobridge server-side technology that
- * this instance is participating in which is establishing the connectivity
- * with the Jitsi Videobridge server (as opposed to a <tt>CallPeer</tt>).
- *
- * @return a <tt>TransportManagerJabberImpl</tt> which is participating in
- * a telephony conference utilizing the Jitsi Videobridge server-side
- * technology that this instance is participating in which is establishing
- * the connectivity with the Jitsi Videobridge server (as opposed to a
- * <tt>CallPeer</tt>).
- */
- TransportManagerJabberImpl
- findTransportManagerEstablishingConnectivityWithJitsiVideobridge()
- {
- Call call = getCallPeer().getCall();
- TransportManagerJabberImpl transportManager = null;
-
- if (call != null)
- {
- CallConference conference = call.getConference();
-
- if ((conference != null) && conference.isJitsiVideobridge())
- {
- for (Call aCall : conference.getCalls())
- {
- Iterator<? extends CallPeer> callPeerIter
- = aCall.getCallPeers();
-
- while (callPeerIter.hasNext())
- {
- CallPeer aCallPeer = callPeerIter.next();
-
- if (aCallPeer instanceof CallPeerJabberImpl)
- {
- TransportManagerJabberImpl aTransportManager
- = ((CallPeerJabberImpl) aCallPeer)
- .getMediaHandler()
- .getTransportManager();
-
- if (aTransportManager
- .isEstablishingConnectivityWithJitsiVideobridge)
- {
- transportManager = aTransportManager;
- break;
- }
- }
- }
- }
- }
- }
- return transportManager;
- }
-
- /**
- * Gets the {@link ColibriConferenceIQ.Channel} which belongs to a content
- * associated with a specific <tt>MediaType</tt> and is to be either locally
- * or remotely used.
- * <p>
- * <b>Note</b>: Modifications to the <tt>ColibriConferenceIQ.Channel</tt>
- * instance returned by the method propagate to (the state of) this
- * instance.
- * </p>
- *
- * @param mediaType the <tt>MediaType</tt> associated with the content which
- * contains the <tt>ColibriConferenceIQ.Channel</tt> to get
- * @param local <tt>true</tt> if the <tt>ColibriConferenceIQ.Channel</tt>
- * which is to be used locally is to be returned or <tt>false</tt> for the
- * one which is to be used remotely
- * @return the <tt>ColibriConferenceIQ.Channel</tt> which belongs to a
- * content associated with the specified <tt>mediaType</tt> and which is to
- * be used in accord with the specified <tt>local</tt> indicator if such a
- * channel exists; otherwise, <tt>null</tt>
- */
- ColibriConferenceIQ.Channel getColibriChannel(
- MediaType mediaType,
- boolean local)
- {
- ColibriConferenceIQ.Channel channel = null;
-
- if (colibri != null)
- {
- ColibriConferenceIQ.Content content
- = colibri.getContent(mediaType.toString());
-
- if (content != null)
- {
- List<ColibriConferenceIQ.Channel> channels
- = content.getChannels();
-
- if (channels.size() == 2)
- channel = channels.get(local ? 0 : 1);
- }
- }
- return channel;
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber; + +import java.net.*; +import java.util.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.media.*; +import net.java.sip.communicator.util.*; + +import org.jitsi.service.neomedia.*; +import org.jivesoftware.smack.packet.*; + +/** + * <tt>TransportManager</tt>s gather local candidates for incoming and outgoing + * calls. Their work starts by calling a start method which, using the remote + * peer's session description, would start the harvest. Calling a second wrapup + * method would deliver the candidate harvest, possibly after blocking if it has + * not yet completed. + * + * @author Emil Ivov + * @author Lyubomir Marinov + */ +public abstract class TransportManagerJabberImpl + extends TransportManager<CallPeerJabberImpl> +{ + /** + * The <tt>Logger</tt> used by the <tt>TransportManagerJabberImpl</tt> class + * and its instances to print debug messages. + */ + private static final Logger logger + = Logger.getLogger(TransportManagerJabberImpl.class); + + /** + * The ID that we will be assigning to our next candidate. We use + * <tt>int</tt>s for interoperability reasons (Emil: I believe that GTalk + * uses <tt>int</tt>s. If that turns out not to be the case we can stop + * using <tt>int</tt>s here if that's an issue). + */ + private static int nextID = 1; + + /** + * The information pertaining to the Jisti Videobridge conference which the + * local peer represented by this instance is a focus of. It gives a view of + * the whole Jitsi Videobridge conference managed by the associated + * <tt>CallJabberImpl</tt> which provides information specific to this + * <tt>TransportManager</tt> only. + */ + private ColibriConferenceIQ colibri; + + /** + * The generation of the candidates we are currently generating + */ + private int currentGeneration = 0; + + /** + * The indicator which determines whether this <tt>TransportManager</tt> + * instance is responsible to establish the connectivity with the associated + * Jitsi Videobridge (in case it is being employed at all). + */ + boolean isEstablishingConnectivityWithJitsiVideobridge = false; + + /** + * The indicator which determines whether this <tt>TransportManager</tt> + * instance is yet to start establishing the connectivity with the + * associated Jitsi Videobridge (in case it is being employed at all). + */ + boolean startConnectivityEstablishmentWithJitsiVideobridge = false; + + /** + * Creates a new instance of this transport manager, binding it to the + * specified peer. + * + * @param callPeer the {@link CallPeer} whose traffic we will be taking + * care of. + */ + protected TransportManagerJabberImpl(CallPeerJabberImpl callPeer) + { + super(callPeer); + } + + /** + * Returns the <tt>InetAddress</tt> that is most likely to be to be used + * as a next hop when contacting the specified <tt>destination</tt>. This is + * an utility method that is used whenever we have to choose one of our + * local addresses to put in the Via, Contact or (in the case of no + * registrar accounts) From headers. + * + * @param peer the CallPeer that we would contact. + * + * @return the <tt>InetAddress</tt> that is most likely to be to be used + * as a next hop when contacting the specified <tt>destination</tt>. + * + * @throws IllegalArgumentException if <tt>destination</tt> is not a valid + * host/IP/FQDN + */ + @Override + protected InetAddress getIntendedDestination(CallPeerJabberImpl peer) + { + return peer.getProtocolProvider().getNextHop(); + } + + /** + * Returns the ID that we will be assigning to the next candidate we create. + * + * @return the next ID to use with a candidate. + */ + protected String getNextID() + { + int nextID; + + synchronized (TransportManagerJabberImpl.class) + { + nextID = TransportManagerJabberImpl.nextID++; + } + return Integer.toString(nextID); + } + + /** + * Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of + * the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>. + * + * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which + * is to have its <tt>target</tt> set to the returned + * <tt>MediaStreamTarget</tt> + * @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> + * of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt> + */ + public abstract MediaStreamTarget getStreamTarget(MediaType mediaType); + + /** + * Gets the XML namespace of the Jingle transport implemented by this + * <tt>TransportManagerJabberImpl</tt>. + * + * @return the XML namespace of the Jingle transport implemented by this + * <tt>TransportManagerJabberImpl</tt> + */ + public abstract String getXmlNamespace(); + + /** + * Returns the generation that our current candidates belong to. + * + * @return the generation that we should assign to candidates that we are + * currently advertising. + */ + protected int getCurrentGeneration() + { + return currentGeneration; + } + + /** + * Increments the generation that we are assigning candidates. + */ + protected void incrementGeneration() + { + currentGeneration++; + } + + /** + * Sends transport-related information received from the remote peer to the + * associated Jiitsi Videobridge in order to update the (remote) + * <tt>ColibriConferenceIQ.Channel</tt> associated with this + * <tt>TransportManager</tt> instance. + * + * @param map a <tt>Map</tt> of media-IceUdpTransportPacketExtension pairs + * which represents the transport-related information which has been + * received from the remote peer and which is to be sent to the associated + * Jitsi Videobridge + */ + protected void sendTransportInfoToJitsiVideobridge( + Map<String,IceUdpTransportPacketExtension> map) + { + CallPeerJabberImpl peer = getCallPeer(); + boolean initiator = !peer.isInitiator(); + ColibriConferenceIQ conferenceRequest = null; + + for (Map.Entry<String,IceUdpTransportPacketExtension> e + : map.entrySet()) + { + String media = e.getKey(); + MediaType mediaType = MediaType.parseString(media); + ColibriConferenceIQ.Channel channel + = getColibriChannel(mediaType, false /* remote */); + + if (channel != null) + { + IceUdpTransportPacketExtension transport; + + try + { + transport = cloneTransportAndCandidates(e.getValue()); + } + catch (OperationFailedException ofe) + { + transport = null; + } + if (transport == null) + continue; + + ColibriConferenceIQ.Channel channelRequest + = new ColibriConferenceIQ.Channel(); + + channelRequest.setID(channel.getID()); + channelRequest.setInitiator(initiator); + channelRequest.setTransport(transport); + + if (conferenceRequest == null) + { + if (colibri == null) + break; + else + { + String id = colibri.getID(); + + if ((id == null) || (id.length() == 0)) + break; + else + { + conferenceRequest = new ColibriConferenceIQ(); + conferenceRequest.setID(id); + conferenceRequest.setTo(colibri.getFrom()); + conferenceRequest.setType(IQ.Type.SET); + } + } + } + conferenceRequest.getOrCreateContent(media).addChannel( + channelRequest); + } + } + if (conferenceRequest != null) + { + peer.getProtocolProvider().getConnection().sendPacket( + conferenceRequest); + } + } + + /** + * Starts transport candidate harvest for a specific + * <tt>ContentPacketExtension</tt> that we are going to offer or answer + * with. + * + * @param theirContent the <tt>ContentPacketExtension</tt> offered by the + * remote peer to which we are going to answer with <tt>ourContent</tt> or + * <tt>null</tt> if <tt>ourContent</tt> will be an offer to the remote peer + * @param ourContent the <tt>ContentPacketExtension</tt> for which transport + * candidate harvest is to be started + * @param transportInfoSender a <tt>TransportInfoSender</tt> if the + * harvested transport candidates are to be sent in a + * <tt>transport-info</tt> rather than in <tt>ourContent</tt>; otherwise, + * <tt>null</tt> + * @param media the media of the <tt>RtpDescriptionPacketExtension</tt> + * child of <tt>ourContent</tt> + * @return a <tt>PacketExtension</tt> to be added as a child to + * <tt>ourContent</tt>; otherwise, <tt>null</tt> + * @throws OperationFailedException if anything goes wrong while starting + * transport candidate harvest for the specified <tt>ourContent</tt> + */ + protected abstract PacketExtension startCandidateHarvest( + ContentPacketExtension theirContent, + ContentPacketExtension ourContent, + TransportInfoSender transportInfoSender, + String media) + throws OperationFailedException; + + /** + * Starts transport candidate harvest. This method should complete rapidly + * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests + * are necessary, they should be executed in a separate thread. Candidate + * harvest would then need to be concluded in the + * {@link #wrapupCandidateHarvest()} method which would be called once we + * absolutely need the candidates. + * + * @param theirOffer a media description offer that we've received from the + * remote party and that we should use in case we need to know what + * transports our peer is using. + * @param ourAnswer the content descriptions that we should be adding our + * transport lists to (although not necessarily in this very instance). + * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by + * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt> + * <tt>JingleIQ</tt>s from the local peer to the remote peer if this + * <tt>TransportManagerJabberImpl</tt> wishes to utilize + * <tt>transport-info</tt>. Local candidate addresses sent by this + * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are + * expected to not be included in the result of + * {@link #wrapupCandidateHarvest()}. + * + * @throws OperationFailedException if we fail to allocate a port number. + */ + public void startCandidateHarvest( + List<ContentPacketExtension> theirOffer, + List<ContentPacketExtension> ourAnswer, + TransportInfoSender transportInfoSender) + throws OperationFailedException + { + CallPeerJabberImpl peer = getCallPeer(); + CallJabberImpl call = peer.getCall(); + boolean isJitsiVideobridge = call.getConference().isJitsiVideobridge(); + List<ContentPacketExtension> cpes + = (theirOffer == null) ? ourAnswer : theirOffer; + + /* + * If Jitsi Videobridge is to be used, determine which channels are to + * be allocated and attempt to allocate them now. + */ + if (isJitsiVideobridge) + { + Map<ContentPacketExtension,ContentPacketExtension> contentMap + = new LinkedHashMap + <ContentPacketExtension,ContentPacketExtension>(); + + for (ContentPacketExtension cpe : cpes) + { + MediaType mediaType = JingleUtils.getMediaType(cpe); + + /* + * The existence of a content for the mediaType and regardless + * of the existence of channels in it signals that a channel + * allocation request has already been sent for that mediaType. + */ + if ((colibri == null) + || (colibri.getContent(mediaType.toString()) == null)) + { + ContentPacketExtension local, remote; + + if (cpes == ourAnswer) + { + local = cpe; + remote + = (theirOffer == null) + ? null + : findContentByName(theirOffer, cpe.getName()); + } + else + { + local = findContentByName(ourAnswer, cpe.getName()); + remote = cpe; + } + contentMap.put(local, remote); + } + } + if (!contentMap.isEmpty()) + { + /* + * We are about to request the channel allocations for the media + * types found in contentMap. Regardless of the response, we do + * not want to repeat these requests. + */ + if (colibri == null) + colibri = new ColibriConferenceIQ(); + for (Map.Entry<ContentPacketExtension,ContentPacketExtension> e + : contentMap.entrySet()) + { + ContentPacketExtension cpe = e.getValue(); + + if (cpe == null) + cpe = e.getKey(); + + colibri.getOrCreateContent( + JingleUtils.getMediaType(cpe).toString()); + } + + ColibriConferenceIQ conferenceResult + = call.createColibriChannels(peer, contentMap); + + if (conferenceResult != null) + { + String videobridgeID = colibri.getID(); + String conferenceResultID = conferenceResult.getID(); + + if (videobridgeID == null) + colibri.setID(conferenceResultID); + else if (!videobridgeID.equals(conferenceResultID)) + throw new IllegalStateException("conference.id"); + + String videobridgeFrom = conferenceResult.getFrom(); + + if ((videobridgeFrom != null) + && (videobridgeFrom.length() != 0)) + { + colibri.setFrom(videobridgeFrom); + } + + for (ColibriConferenceIQ.Content contentResult + : conferenceResult.getContents()) + { + ColibriConferenceIQ.Content content + = colibri.getOrCreateContent( + contentResult.getName()); + + for (ColibriConferenceIQ.Channel channelResult + : contentResult.getChannels()) + { + if (content.getChannel(channelResult.getID()) + == null) + { + content.addChannel(channelResult); + } + } + } + } + else + { + /* + * The call fails if the createColibriChannels method fails + * which may happen if the conference packet times out or it + * can't be built. + */ + ProtocolProviderServiceJabberImpl + .throwOperationFailedException( + "Failed to allocate colibri channel.", + OperationFailedException.GENERAL_ERROR, + null, + logger); + } + } + } + + for (ContentPacketExtension cpe : cpes) + { + String contentName = cpe.getName(); + ContentPacketExtension ourContent + = findContentByName(ourAnswer, contentName); + + //it might be that we decided not to reply to this content + if (ourContent != null) + { + ContentPacketExtension theirContent + = (theirOffer == null) + ? null + : findContentByName(theirOffer, contentName); + RtpDescriptionPacketExtension rtpDesc + = ourContent.getFirstChildOfType( + RtpDescriptionPacketExtension.class); + String media = rtpDesc.getMedia(); + PacketExtension pe + = startCandidateHarvest( + theirContent, + ourContent, + transportInfoSender, + media); + + if (pe != null) + ourContent.addChildExtension(pe); + } + } + } + + /** + * Starts transport candidate harvest. This method should complete rapidly + * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests + * are necessary, they should be executed in a separate thread. Candidate + * harvest would then need to be concluded in the + * {@link #wrapupCandidateHarvest()} method which would be called once we + * absolutely need the candidates. + * + * @param ourOffer the content descriptions that we should be adding our + * transport lists to (although not necessarily in this very instance). + * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by + * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt> + * <tt>JingleIQ</tt>s from the local peer to the remote peer if this + * <tt>TransportManagerJabberImpl</tt> wishes to utilize + * <tt>transport-info</tt>. Local candidate addresses sent by this + * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are + * expected to not be included in the result of + * {@link #wrapupCandidateHarvest()}. + * @throws OperationFailedException if we fail to allocate a port number. + */ + public void startCandidateHarvest( + List<ContentPacketExtension> ourOffer, + TransportInfoSender transportInfoSender) + throws OperationFailedException + { + startCandidateHarvest( + /* theirOffer */ null, + ourOffer, + transportInfoSender); + } + + /** + * Notifies the transport manager that it should conclude candidate + * harvesting as soon as possible and return the lists of candidates + * gathered so far. + * + * @return the content list that we received earlier (possibly cloned into + * a new instance) and that we have updated with transport lists. + */ + public abstract List<ContentPacketExtension> wrapupCandidateHarvest(); + + /** + * Looks through the <tt>cpExtList</tt> and returns the {@link + * ContentPacketExtension} with the specified name. + * + * @param cpExtList the list that we will be searching for a specific + * content. + * @param name the name of the content element we are looking for. + * @return the {@link ContentPacketExtension} with the specified name or + * <tt>null</tt> if no such content element exists. + */ + public static ContentPacketExtension findContentByName( + Iterable<ContentPacketExtension> cpExtList, + String name) + { + for(ContentPacketExtension cpExt : cpExtList) + { + if(cpExt.getName().equals(name)) + return cpExt; + } + return null; + } + + /** + * Starts the connectivity establishment of this + * <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between + * the local and the remote peers given the remote counterpart of the + * negotiation between them. + * + * @param remote the collection of <tt>ContentPacketExtension</tt>s which + * represents the remote counterpart of the negotiation between the local + * and the remote peer + * @return <tt>true</tt> if connectivity establishment has been started in + * response to the call; otherwise, <tt>false</tt>. + * <tt>TransportManagerJabberImpl</tt> implementations which do not perform + * connectivity checks (e.g. raw UDP) should return <tt>true</tt>. The + * default implementation does not perform connectivity checks and always + * returns <tt>true</tt>. + */ + public boolean startConnectivityEstablishment( + Iterable<ContentPacketExtension> remote) + { + return true; + } + + /** + * Starts the connectivity establishment of this + * <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between + * the local and the remote peers given the remote counterpart of the + * negotiation between them. + * + * @param remote a <tt>Map</tt> of + * media-<tt>IceUdpTransportPacketExtension</tt> pairs which represents the + * remote counterpart of the negotiation between the local and the remote + * peers + * @return <tt>true</tt> if connectivity establishment has been started in + * response to the call; otherwise, <tt>false</tt>. + * <tt>TransportManagerJabberImpl</tt> implementations which do not perform + * connectivity checks (e.g. raw UDP) should return <tt>true</tt>. The + * default implementation does not perform connectivity checks and always + * returns <tt>true</tt>. + */ + protected boolean startConnectivityEstablishment( + Map<String,IceUdpTransportPacketExtension> remote) + { + return true; + } + + /** + * Notifies this <tt>TransportManagerJabberImpl</tt> that it should conclude + * any started connectivity establishment. + * + * @throws OperationFailedException if anything goes wrong with connectivity + * establishment (i.e. ICE failed, ...) + */ + public void wrapupConnectivityEstablishment() + throws OperationFailedException + { + } + + /** + * Removes a content with a specific name from the transport-related part of + * the session represented by this <tt>TransportManagerJabberImpl</tt> which + * may have been reported through previous calls to the + * <tt>startCandidateHarvest</tt> and + * <tt>startConnectivityEstablishment</tt> methods. + * <p> + * <b>Note</b>: Because <tt>TransportManager</tt> deals with + * <tt>MediaType</tt>s, not content names and + * <tt>TransportManagerJabberImpl</tt> does not implement translating from + * content name to <tt>MediaType</tt>, implementers are expected to call + * {@link TransportManager#closeStreamConnector(MediaType)}. + * </p> + * + * @param name the name of the content to be removed from the + * transport-related part of the session represented by this + * <tt>TransportManagerJabberImpl</tt> + */ + public abstract void removeContent(String name); + + /** + * Removes a content with a specific name from a specific collection of + * contents and closes any associated <tt>StreamConnector</tt>. + * + * @param contents the collection of contents to remove the content with the + * specified name from + * @param name the name of the content to remove + * @return the removed <tt>ContentPacketExtension</tt> if any; otherwise, + * <tt>null</tt> + */ + protected ContentPacketExtension removeContent( + Iterable<ContentPacketExtension> contents, + String name) + { + for (Iterator<ContentPacketExtension> contentIter = contents.iterator(); + contentIter.hasNext();) + { + ContentPacketExtension content = contentIter.next(); + + if (name.equals(content.getName())) + { + contentIter.remove(); + + // closeStreamConnector + MediaType mediaType = JingleUtils.getMediaType(content); + if (mediaType != null) + { + closeStreamConnector(mediaType); + } + + return content; + } + } + return null; + } + + /** + * Clones a specific <tt>IceUdpTransportPacketExtension</tt> and its + * candidates. + * + * @param src the <tt>IceUdpTransportPacketExtension</tt> to be cloned + * @return a new <tt>IceUdpTransportPacketExtension</tt> instance which has + * the same run-time type, attributes, namespace, text and candidates as the + * specified <tt>src</tt> + * @throws OperationFailedException if an error occurs during the cloing of + * the specified <tt>src</tt> and its candidates + */ + static IceUdpTransportPacketExtension cloneTransportAndCandidates( + IceUdpTransportPacketExtension src) + throws OperationFailedException + { + try + { + return IceUdpTransportPacketExtension + .cloneTransportAndCandidates(src); + } + catch (Exception e) + { + ProtocolProviderServiceJabberImpl + .throwOperationFailedException( + "Failed to close transport and candidates.", + OperationFailedException.GENERAL_ERROR, + e, + logger); + + } + return null; + } + + /** + * Releases the resources acquired by this <tt>TransportManager</tt> and + * prepares it for garbage collection. + */ + public void close() + { + for (MediaType mediaType : MediaType.values()) + closeStreamConnector(mediaType); + } + + /** + * Closes a specific <tt>StreamConnector</tt> associated with a specific + * <tt>MediaType</tt>. If this <tt>TransportManager</tt> has a reference to + * the specified <tt>streamConnector</tt>, it remains. + * Also expires the <tt>ColibriConferenceIQ.Channel</tt> associated with + * the closed <tt>StreamConnector</tt>. + * + * @param mediaType the <tt>MediaType</tt> associated with the specified + * <tt>streamConnector</tt> + * @param streamConnector the <tt>StreamConnector</tt> to be closed + */ + @Override + protected void closeStreamConnector( + MediaType mediaType, + StreamConnector streamConnector) + { + try + { + boolean superCloseStreamConnector = true; + + if (streamConnector instanceof ColibriStreamConnector) + { + CallPeerJabberImpl peer = getCallPeer(); + + if (peer != null) + { + CallJabberImpl call = peer.getCall(); + + if (call != null) + { + superCloseStreamConnector = false; + call.closeColibriStreamConnector( + peer, + mediaType, + (ColibriStreamConnector) streamConnector); + } + } + } + if (superCloseStreamConnector) + super.closeStreamConnector(mediaType, streamConnector); + } + finally + { + /* + * Expire the ColibriConferenceIQ.Channel associated with the closed + * StreamConnector. + */ + if (colibri != null) + { + ColibriConferenceIQ.Content content + = colibri.getContent(mediaType.toString()); + + if (content != null) + { + List<ColibriConferenceIQ.Channel> channels + = content.getChannels(); + + if (channels.size() == 2) + { + ColibriConferenceIQ requestConferenceIQ + = new ColibriConferenceIQ(); + + requestConferenceIQ.setID(colibri.getID()); + + ColibriConferenceIQ.Content requestContent + = requestConferenceIQ.getOrCreateContent( + content.getName()); + + requestContent.addChannel(channels.get(1 /* remote */)); + + /* + * Regardless of whether the request to expire the + * Channel associated with mediaType succeeds, consider + * the Channel in question expired. Since + * RawUdpTransportManager allocates a single channel per + * MediaType, consider the whole Content expired. + */ + colibri.removeContent(content); + + CallPeerJabberImpl peer = getCallPeer(); + + if (peer != null) + { + CallJabberImpl call = peer.getCall(); + + if (call != null) + { + call.expireColibriChannels( + peer, + requestConferenceIQ); + } + } + } + } + } + } + } + + /** + * {@inheritDoc} + * + * Adds support for telephony conferences utilizing the Jitsi Videobridge + * server-side technology. + * + * @see #doCreateStreamConnector(MediaType) + */ + @Override + protected StreamConnector createStreamConnector(final MediaType mediaType) + throws OperationFailedException + { + ColibriConferenceIQ.Channel channel + = getColibriChannel(mediaType, true /* local */); + + if (channel != null) + { + CallPeerJabberImpl peer = getCallPeer(); + CallJabberImpl call = peer.getCall(); + StreamConnector streamConnector + = call.createColibriStreamConnector( + peer, + mediaType, + channel, + new StreamConnectorFactory() + { + public StreamConnector createStreamConnector() + { + try + { + return doCreateStreamConnector(mediaType); + } + catch (OperationFailedException ofe) + { + return null; + } + } + }); + + if (streamConnector != null) + return streamConnector; + } + + return doCreateStreamConnector(mediaType); + } + + protected abstract PacketExtension createTransport(String media) + throws OperationFailedException; + + protected PacketExtension createTransportForStartCandidateHarvest( + String media) + throws OperationFailedException + { + PacketExtension pe = null; + + if (getCallPeer().isJitsiVideobridge()) + { + MediaType mediaType = MediaType.parseString(media); + ColibriConferenceIQ.Channel channel + = getColibriChannel(mediaType, false /* remote */); + + if (channel != null) + pe = cloneTransportAndCandidates(channel.getTransport()); + } + else + pe = createTransport(media); + return pe; + } + + /** + * Initializes a new <tt>PacketExtension</tt> instance appropriate to the + * type of Jingle transport represented by this <tt>TransportManager</tt>. + * The new instance is not initialized with any attributes or child + * extensions. + * + * @return a new <tt>PacketExtension</tt> instance appropriate to the type + * of Jingle transport represented by this <tt>TransportManager</tt> + */ + protected abstract PacketExtension createTransportPacketExtension(); + + /** + * Creates a media <tt>StreamConnector</tt> for a stream of a specific + * <tt>MediaType</tt>. The minimum and maximum of the media port boundaries + * are taken into account. + * + * @param mediaType the <tt>MediaType</tt> of the stream for which a + * <tt>StreamConnector</tt> is to be created + * @return a <tt>StreamConnector</tt> for the stream of the specified + * <tt>mediaType</tt> + * @throws OperationFailedException if the binding of the sockets fails + */ + protected StreamConnector doCreateStreamConnector(MediaType mediaType) + throws OperationFailedException + { + return super.createStreamConnector(mediaType); + } + + /** + * Finds a <tt>TransportManagerJabberImpl</tt> participating in a telephony + * conference utilizing the Jitsi Videobridge server-side technology that + * this instance is participating in which is establishing the connectivity + * with the Jitsi Videobridge server (as opposed to a <tt>CallPeer</tt>). + * + * @return a <tt>TransportManagerJabberImpl</tt> which is participating in + * a telephony conference utilizing the Jitsi Videobridge server-side + * technology that this instance is participating in which is establishing + * the connectivity with the Jitsi Videobridge server (as opposed to a + * <tt>CallPeer</tt>). + */ + TransportManagerJabberImpl + findTransportManagerEstablishingConnectivityWithJitsiVideobridge() + { + Call call = getCallPeer().getCall(); + TransportManagerJabberImpl transportManager = null; + + if (call != null) + { + CallConference conference = call.getConference(); + + if ((conference != null) && conference.isJitsiVideobridge()) + { + for (Call aCall : conference.getCalls()) + { + Iterator<? extends CallPeer> callPeerIter + = aCall.getCallPeers(); + + while (callPeerIter.hasNext()) + { + CallPeer aCallPeer = callPeerIter.next(); + + if (aCallPeer instanceof CallPeerJabberImpl) + { + TransportManagerJabberImpl aTransportManager + = ((CallPeerJabberImpl) aCallPeer) + .getMediaHandler() + .getTransportManager(); + + if (aTransportManager + .isEstablishingConnectivityWithJitsiVideobridge) + { + transportManager = aTransportManager; + break; + } + } + } + } + } + } + return transportManager; + } + + /** + * Gets the {@link ColibriConferenceIQ.Channel} which belongs to a content + * associated with a specific <tt>MediaType</tt> and is to be either locally + * or remotely used. + * <p> + * <b>Note</b>: Modifications to the <tt>ColibriConferenceIQ.Channel</tt> + * instance returned by the method propagate to (the state of) this + * instance. + * </p> + * + * @param mediaType the <tt>MediaType</tt> associated with the content which + * contains the <tt>ColibriConferenceIQ.Channel</tt> to get + * @param local <tt>true</tt> if the <tt>ColibriConferenceIQ.Channel</tt> + * which is to be used locally is to be returned or <tt>false</tt> for the + * one which is to be used remotely + * @return the <tt>ColibriConferenceIQ.Channel</tt> which belongs to a + * content associated with the specified <tt>mediaType</tt> and which is to + * be used in accord with the specified <tt>local</tt> indicator if such a + * channel exists; otherwise, <tt>null</tt> + */ + ColibriConferenceIQ.Channel getColibriChannel( + MediaType mediaType, + boolean local) + { + ColibriConferenceIQ.Channel channel = null; + + if (colibri != null) + { + ColibriConferenceIQ.Content content + = colibri.getContent(mediaType.toString()); + + if (content != null) + { + List<ColibriConferenceIQ.Channel> channels + = content.getChannels(); + + if (channels.size() == 2) + channel = channels.get(local ? 0 : 1); + } + } + return channel; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/DefaultPacketExtensionProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/DefaultPacketExtensionProvider.java index 1285581..778d086 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/DefaultPacketExtensionProvider.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/DefaultPacketExtensionProvider.java @@ -19,6 +19,7 @@ package net.java.sip.communicator.impl.protocol.jabber.extensions; import java.util.logging.*; +import net.java.sip.communicator.service.protocol.jabber.*; import org.jivesoftware.smack.packet.*; import org.jivesoftware.smack.provider.*; import org.xmlpull.v1.*; @@ -41,6 +42,13 @@ public class DefaultPacketExtensionProvider<C extends AbstractPacketExtension> .getLogger(DefaultPacketExtensionProvider.class.getName()); /** + * The <tt>AbstractSmackInteroperabilityLayer</tt> instance implementing + * necessary methods + */ + private AbstractSmackInteroperabilityLayer smackInteroperabilityLayer = + AbstractSmackInteroperabilityLayer.getInstance(); + + /** * The {@link Class} that the packets we will be parsing here belong to. */ private final Class<C> packetClass; @@ -100,8 +108,7 @@ public class DefaultPacketExtensionProvider<C extends AbstractPacketExtension> if (eventType == XmlPullParser.START_TAG) { - PacketExtensionProvider provider - = (PacketExtensionProvider)ProviderManager.getInstance() + PacketExtensionProvider provider = smackInteroperabilityLayer .getExtensionProvider( elementName, namespace ); if(provider == null) diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java index ebdcbb0..97d9ff8 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java @@ -243,6 +243,7 @@ public class EntityCapsManager if ((user != null) && (node != null) && (hash != null) && (ver != null)) { Caps caps = userCaps.get(user); + String bareJid=StringUtils.parseBareAddress(user); if ((caps == null) || !caps.node.equals(node) @@ -270,7 +271,9 @@ public class EntityCapsManager String nodeVer = caps.getNodeVer(); for (UserCapsNodeListener listener : listeners) - listener.userCapsNodeAdded(user, nodeVer, online); + listener.userCapsNodeAdded(user, + getFullJidsByBareJid(bareJid), + nodeVer, online); } } } @@ -305,6 +308,8 @@ public class EntityCapsManager { Caps caps = null; String lastRemovedJid = null; + String bareJid=StringUtils.parseBareAddress( + contact.getAddress()); Iterator<String> iter = userCaps.keySet().iterator(); while(iter.hasNext()) @@ -337,7 +342,9 @@ public class EntityCapsManager for (UserCapsNodeListener listener : listeners) listener.userCapsNodeRemoved( - lastRemovedJid, nodeVer, false); + lastRemovedJid, + getFullJidsByBareJid(bareJid), + nodeVer, false); } } } @@ -350,6 +357,7 @@ public class EntityCapsManager public void removeUserCapsNode(String user) { Caps caps = userCaps.remove(user); + String bareJid=StringUtils.parseBareAddress(user); // Fire userCapsNodeRemoved. if (caps != null) @@ -367,7 +375,9 @@ public class EntityCapsManager String nodeVer = caps.getNodeVer(); for (UserCapsNodeListener listener : listeners) - listener.userCapsNodeRemoved(user, nodeVer, false); + listener.userCapsNodeRemoved(user, + getFullJidsByBareJid(bareJid), + nodeVer, false); } } } @@ -404,6 +414,24 @@ public class EntityCapsManager { return userCaps.get(user); } + + /** + * Gets the full Jids (with resources) as Strings. + * + * @param the bare Jid + * @return the full Jids as an ArrayList <tt>user</tt> + */ + public ArrayList<String> getFullJidsByBareJid(String bareJid) + { + ArrayList<String> jids = new ArrayList<String>(); + for(String jid: userCaps.keySet()) + { + if(bareJid.equals(StringUtils.parseBareAddress(jid))){ + jids.add(jid); + } + } + return jids; + } /** * Get the discover info given a user name. The discover info is returned if @@ -605,14 +633,9 @@ public class EntityCapsManager * @param connection the connection that we'd like this manager to register * with. */ - public void addPacketListener(XMPPConnection connection) + public void addPacketListener(Connection connection) { - PacketFilter filter - = new AndFilter( - new PacketTypeFilter(Presence.class), - new PacketExtensionFilter( - CapsPacketExtension.ELEMENT_NAME, - CapsPacketExtension.NAMESPACE)); + PacketFilter filter = new PacketTypeFilter(Presence.class); connection.addPacketListener(new CapsPacketListener(), filter); } @@ -913,48 +936,48 @@ public class EntityCapsManager */ public void processPacket(Packet packet) { + // Check it the packet indicates that the user is online. We + // will use this information to decide if we're going to send + // the discover info request. + boolean online + = (packet instanceof Presence) + && ((Presence) packet).isAvailable(); + CapsPacketExtension ext = (CapsPacketExtension) packet.getExtension( CapsPacketExtension.ELEMENT_NAME, CapsPacketExtension.NAMESPACE); - /* - * Before Version 1.4 of XEP-0115: Entity Capabilities, the 'ver' - * attribute was generated differently and the 'hash' attribute was - * absent. The 'ver' attribute in Version 1.3 represents the - * specific version of the client and thus does not provide a way to - * validate the DiscoverInfo sent by the client. If - * EntityCapsManager receives no 'hash' attribute, it will assume - * the legacy format and will not cache it because the DiscoverInfo - * to be received from the client later on will not be trustworthy. - */ - String hash = ext.getHash(); - - /* Google Talk web does not set hash but we need it to be cached */ - if(hash == null) - hash = ""; - - if (hash != null) + if(ext != null && online) { - // Check it the packet indicates that the user is online. We - // will use this information to decide if we're going to send - // the discover info request. - boolean online - = (packet instanceof Presence) - && ((Presence) packet).isAvailable(); - - if(online) - { - addUserCapsNode( - packet.getFrom(), - ext.getNode(), hash, ext.getVersion(), - ext.getExtensions(), online); - } - else - { - removeUserCapsNode(packet.getFrom()); - } + /* + * Before Version 1.4 of XEP-0115: Entity Capabilities, + * the 'ver' attribute was generated differently and the 'hash' + * attribute was absent. The 'ver' attribute in Version 1.3 + * represents the specific version of the client and thus does + * not provide a way to validate the DiscoverInfo sent by + * the client. If EntityCapsManager receives no 'hash' + * attribute, it will assume the legacy format and will not + * cache it because the DiscoverInfo to be received from + * the client later on will not be trustworthy. + */ + String hash = ext.getHash(); + + /* Google Talk web does not set hash, but we need it to + * be cached + */ + if (hash == null) + hash = ""; + + addUserCapsNode( + packet.getFrom(), + ext.getNode(), hash, ext.getVersion(), + ext.getExtensions(), online); + } + else if (!online) + { + removeUserCapsNode(packet.getFrom()); } } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/UserCapsNodeListener.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/UserCapsNodeListener.java index 5ee38b0..eda921f 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/UserCapsNodeListener.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/UserCapsNodeListener.java @@ -17,6 +17,8 @@ */ package net.java.sip.communicator.impl.protocol.jabber.extensions.caps; +import java.util.ArrayList; + /** * Represents a listener of events notifying about changes in the list of user * caps nodes of <tt>EntityCapsManager</tt>. @@ -30,18 +32,22 @@ public interface UserCapsNodeListener * record for a specific user about the caps node the user has. * * @param user the user (full JID) + * @param fullJids a list of all resources of the user (full JIDs) * @param node the entity caps node#ver * @param online indicates if the user for which we're notified is online */ - public void userCapsNodeAdded(String user, String node, boolean online); + public void userCapsNodeAdded(String user, ArrayList<String> fullJids, + String node, boolean online); /** * Notifies this listener that an <tt>EntityCapsManager</tt> has removed a * record for a specific user about the caps node the user has. * * @param user the user (full JID) + * @param fullJids a list of all resources of the user (full JIDs) * @param node the entity caps node#ver * @param online indicates if the user for which we're notified is online */ - public void userCapsNodeRemoved(String user, String node, boolean online); + public void userCapsNodeRemoved(String user, ArrayList<String> fullJids, + String node, boolean online); } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriBuilder.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriBuilder.java index 547ebe8..48207c2 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriBuilder.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriBuilder.java @@ -37,9 +37,9 @@ import java.util.*; * Add one or multiple requests of the same type by calling * {@link #addAllocateChannelsReq(boolean, String, boolean, java.util.List)}} * or {@link #addExpireChannelsReq(ColibriConferenceIQ)} - * or {@link #addTransportUpdateReq(boolean, java.util.Map, ColibriConferenceIQ)} - * or {@link #addBundleTransportUpdateReq( - * boolean, IceUdpTransportPacketExtension, ColibriConferenceIQ)}. + * or {@link #addRtpDescription(Map, ColibriConferenceIQ)} + * and {@link #addSSSRCGroupsInfo(Map, ColibriConferenceIQ)} + * and {@link #addSSSRCInfo(Map, ColibriConferenceIQ)}. * </li> * <li> * Compile the request by calling {@link #getRequest(String)}. Then send it to @@ -119,6 +119,20 @@ public class ColibriBuilder private SimulcastMode simulcastMode; /** + * Specifies the audio packet delay that will be set on all created audio + * channels. When set to <tt>null</tt> the builder will clear the attribute + * which stands for 'undefined'. + **/ + private Integer audioPacketDelay; + + /** + * Channel 'rtp-level-relay-type' option that will be used with all created + * audio channel. Possible values: mixer or translator (default). + * + */ + private RTPLevelRelayType rtpLevelRelayType; + + /** * Creates new instance of {@link ColibriBuilder} for given * <tt>conferenceState</tt>. * @@ -161,18 +175,26 @@ public class ColibriBuilder * @param contents the list of {@link ContentPacketExtension} describing * channels media. * - * @return this instance fo calls chaining purpose. + * @return <tt>true</tt> if the request yields any changes in Colibri + * channels state on the bridge or <tt>false</tt> otherwise. + * In general when <tt>false</tt> is returned for all + * combined requests it makes no sense to send it. */ - public ColibriBuilder addAllocateChannelsReq( - boolean useBundle, - String endpointName, - boolean peerIsInitiator, + public boolean addAllocateChannelsReq( + boolean useBundle, + String endpointName, + boolean peerIsInitiator, List<ContentPacketExtension> contents) { + Objects.requireNonNull(endpointName, "endpointName"); + Objects.requireNonNull(contents, "contents"); + assertRequestType(RequestType.ALLOCATE_CHANNELS); request.setType(IQ.Type.GET); + boolean hasAnyChanges = false; + for (ContentPacketExtension cpe : contents) { MediaType mediaType = JingleUtils.getMediaType(cpe); @@ -193,7 +215,7 @@ public class ColibriBuilder remoteChannelRequest.setChannelBundleId(endpointName); } - if (mediaType != MediaType.DATA) + if (remoteChannelRequest instanceof ColibriConferenceIQ.Channel) { RtpDescriptionPacketExtension rdpe = cpe.getFirstChildOfType( @@ -213,6 +235,13 @@ public class ColibriBuilder remoteRtpChannelRequest.setAdaptiveLastN(adaptiveLastN); remoteRtpChannelRequest.setAdaptiveSimulcast(adaptiveSimulcast); remoteRtpChannelRequest.setSimulcastMode(simulcastMode); + if (MediaType.AUDIO.equals(mediaType)) + { + // When audioPacketDelay is null it will clear the attribute + remoteRtpChannelRequest.setPacketDelay(audioPacketDelay); + // Set rtp packet relay type for this channel + remoteRtpChannelRequest.setRTPLevelRelayType(rtpLevelRelayType); + } } // Copy transport @@ -221,13 +250,17 @@ public class ColibriBuilder copyTransportOnChannel(cpe, remoteChannelRequest); } - if (mediaType != MediaType.DATA) + if (remoteChannelRequest instanceof ColibriConferenceIQ.Channel) { + hasAnyChanges = true; + contentRequest.addChannel( (ColibriConferenceIQ.Channel) remoteChannelRequest); } else { + hasAnyChanges = true; + contentRequest.addSctpConnection( (ColibriConferenceIQ.SctpConnection) remoteChannelRequest); } @@ -246,6 +279,8 @@ public class ColibriBuilder IceUdpTransportPacketExtension.class); if (transport != null) { + hasAnyChanges = true; + bundle.setTransport( IceUdpTransportPacketExtension .cloneTransportAndCandidates(transport, true)); @@ -254,106 +289,38 @@ public class ColibriBuilder request.addChannelBundle(bundle); } - return this; - } - - /** - * Adds next ICE transport update request to - * {@link RequestType#TRANSPORT_UPDATE} query currently being built. - * - * @param initiator the value that will be set in 'initiator' - * attribute({@link ColibriConferenceIQ.Channel#initiator}). - * @param map the map of content name to transport extensions. Maps - * transport to media types. - * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about - * Colibri channels to be updated. - * - * @return this instance fo calls chaining purpose. - */ - public ColibriBuilder addTransportUpdateReq( - boolean initiator, - Map<String, IceUdpTransportPacketExtension> map, - ColibriConferenceIQ localChannelsInfo) - { - if (conferenceState == null - || StringUtils.isNullOrEmpty(conferenceState.getID())) - { - // We are not initialized yet - return null; - } - - assertRequestType(RequestType.TRANSPORT_UPDATE); - - request.setType(IQ.Type.SET); - - for (Map.Entry<String,IceUdpTransportPacketExtension> e - : map.entrySet()) - { - String contentName = e.getKey(); - ColibriConferenceIQ.ChannelCommon channel - = getColibriChannel(localChannelsInfo, contentName); - - if (channel != null) - { - IceUdpTransportPacketExtension transport - = IceUdpTransportPacketExtension - .cloneTransportAndCandidates(e.getValue(), true); - - ColibriConferenceIQ.ChannelCommon channelRequest - = channel instanceof ColibriConferenceIQ.Channel - ? new ColibriConferenceIQ.Channel() - : new ColibriConferenceIQ.SctpConnection(); - - channelRequest.setID(channel.getID()); - channelRequest.setEndpoint(channel.getEndpoint()); - channelRequest.setInitiator(initiator); - channelRequest.setTransport(transport); - - if (channelRequest instanceof ColibriConferenceIQ.Channel) - { - request.getOrCreateContent(contentName) - .addChannel( - (ColibriConferenceIQ.Channel) channelRequest); - } - else - { - request.getOrCreateContent(contentName) - .addSctpConnection( - (ColibriConferenceIQ.SctpConnection) channelRequest); - } - } - } - return this; + return hasAnyChanges; } /** - * Adds next request to {@link RequestType#BUNDLE_TRANSPORT_UPDATE} query. - * @param initiator the value that will be set in 'initiator' - * attribute({@link ColibriConferenceIQ.Channel#initiator}). + * Adds next request to {@link RequestType#CHANNEL_INFO_UPDATE} query. * @param localChannelsInfo the {@link ColibriConferenceIQ} instance that * describes the channel for which bundle transport will be updated. * It should contain the description of only one "channel bundle". * If it contains more than one then the first one will be used. - * @return this instance for calls chaining purpose. + * @return <tt>true</tt> if the request yields any changes in Colibri + * channels state on the bridge or <tt>false</tt> otherwise. + * In general when <tt>false</tt> is returned for all + * combined requests it makes no sense to send it. * @throws IllegalArgumentException if <tt>localChannelsInfo</tt> does not * describe any channel bundles. */ - public ColibriBuilder addBundleTransportUpdateReq( - boolean initiator, - IceUdpTransportPacketExtension transport, - ColibriConferenceIQ localChannelsInfo) + public boolean addBundleTransportUpdateReq( + IceUdpTransportPacketExtension transport, + ColibriConferenceIQ localChannelsInfo) throws IllegalArgumentException { - // FIXME:'initiator' not used on bundle transport update ? + Objects.requireNonNull(transport, "transport"); + Objects.requireNonNull(localChannelsInfo, "localChannelsInfo"); if (conferenceState == null || StringUtils.isNullOrEmpty(conferenceState.getID())) { // We are not initialized yet - return null; + return false; } - assertRequestType(RequestType.BUNDLE_TRANSPORT_UPDATE); + assertRequestType(RequestType.CHANNEL_INFO_UPDATE); request.setType(IQ.Type.SET); @@ -371,7 +338,7 @@ public class ColibriBuilder else { throw new IllegalArgumentException( - "Expected ChannelBundle as not found"); + "Expected ChannelBundle as not found"); } ColibriConferenceIQ.ChannelBundle bundleUpdate @@ -387,7 +354,7 @@ public class ColibriBuilder request.addChannelBundle(bundleUpdate); - return this; + return true; } /** @@ -395,15 +362,20 @@ public class ColibriBuilder * {@link RequestType#EXPIRE_CHANNELS} query currently being built. * @param channelInfo the {@link ColibriConferenceIQ} instance that contains * info about the channels to be expired. - * @return this instance for the purpose of calls chaining. + * @return <tt>true</tt> if the request yields any changes in Colibri + * channels state on the bridge or <tt>false</tt> otherwise. + * In general when <tt>false</tt> is returned for all + * combined requests it makes no sense to send it. */ - public ColibriBuilder addExpireChannelsReq(ColibriConferenceIQ channelInfo) + public boolean addExpireChannelsReq(ColibriConferenceIQ channelInfo) { + Objects.requireNonNull(channelInfo, "channelInfo"); + // Formulate the ColibriConferenceIQ request which is to be sent. if (conferenceState == null || StringUtils.isNullOrEmpty(conferenceState.getID())) { - return null; + return false; } assertRequestType(RequestType.EXPIRE_CHANNELS); @@ -411,7 +383,7 @@ public class ColibriBuilder request.setType(IQ.Type.SET); for (ColibriConferenceIQ.Content expiredContent - : channelInfo.getContents()) + : channelInfo.getContents()) { ColibriConferenceIQ.Content stateContent = conferenceState.getContent(expiredContent.getName()); @@ -420,7 +392,7 @@ public class ColibriBuilder { ColibriConferenceIQ.Content requestContent = request.getOrCreateContent( - stateContent.getName()); + stateContent.getName()); for (ColibriConferenceIQ.Channel expiredChannel : expiredContent.getChannels()) @@ -490,7 +462,7 @@ public class ColibriBuilder */ /*if (stateContent.getChannelCount() == 1) { - stateChannel = stateContent.getChannel(0); + stateChannel = stateContent.getRtpChannel(0); ColibriConferenceIQ.Channel channelRequest = new ColibriConferenceIQ.Channel(); @@ -537,7 +509,294 @@ public class ColibriBuilder } } - return this; + return hasAnyChannelsToExpire; + } + + /** + * Adds next payload type information update request to + * {@link RequestType#CHANNEL_INFO_UPDATE} query currently being built. + * + * @param map the map of content name to RTP description packet extension. + * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about + * Colibri channels to be updated. + * + * @return <tt>true</tt> if the request yields any changes in Colibri + * channels state on the bridge or <tt>false</tt> otherwise. + * In general when <tt>false</tt> is returned for all + * combined requests it makes no sense to send it. + */ + public boolean addRtpDescription( + Map<String, RtpDescriptionPacketExtension> map, + ColibriConferenceIQ localChannelsInfo) + { + Objects.requireNonNull(map, "map"); + Objects.requireNonNull(localChannelsInfo, "localChannelsInfo"); + + if (conferenceState == null + || StringUtils.isNullOrEmpty(conferenceState.getID())) + { + // We are not initialized yet + return false; + } + + assertRequestType(RequestType.CHANNEL_INFO_UPDATE); + + request.setType(IQ.Type.SET); + + boolean anyUpdates = false; + + for (Map.Entry<String, RtpDescriptionPacketExtension> e + : map.entrySet()) + { + String contentName = e.getKey(); + ColibriConferenceIQ.ChannelCommon channel + = getColibriChannel(localChannelsInfo, contentName); + + if (channel != null + && channel instanceof ColibriConferenceIQ.Channel) + { + RtpDescriptionPacketExtension rtpPE = e.getValue(); + if (rtpPE == null) + { + continue; + } + + List<PayloadTypePacketExtension> pts = rtpPE.getPayloadTypes(); + if (pts == null || pts.isEmpty()) + { + continue; + } + + anyUpdates = true; + + ColibriConferenceIQ.Channel channelRequest + = (ColibriConferenceIQ.Channel) getRequestChannel( + request.getOrCreateContent(contentName), + channel); + if (channelRequest == null) + { + channelRequest = new ColibriConferenceIQ.Channel(); + channelRequest.setID(channel.getID()); + } + + for (PayloadTypePacketExtension ptPE : pts) + { + channelRequest.addPayloadType(ptPE); + } + } + } + + return anyUpdates; + } + + /** + * Adds next SSRC information update request to + * {@link RequestType#CHANNEL_INFO_UPDATE} query currently being built. + * + * @param ssrcMap the map of content name to the list of + * <tt>SourcePacketExtension</tt>. + * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about + * Colibri channels to be updated. + * + * @return <tt>true</tt> if the request yields any changes in Colibri + * channels state on the bridge or <tt>false</tt> otherwise. + * In general when <tt>false</tt> is returned for all + * combined requests it makes no sense to send it. + */ + public boolean addSSSRCInfo( + Map<String, List<SourcePacketExtension>> ssrcMap, + ColibriConferenceIQ localChannelsInfo) + { + Objects.requireNonNull(ssrcMap, "ssrcMap"); + Objects.requireNonNull(localChannelsInfo, "localChannelsInfo"); + + if (conferenceState == null + || StringUtils.isNullOrEmpty(conferenceState.getID())) + { + // We are not initialized yet + return false; + } + + assertRequestType(RequestType.CHANNEL_INFO_UPDATE); + + request.setType(IQ.Type.SET); + + boolean anyUpdates = false; + + // Go over SSRCs + for (String contentName : ssrcMap.keySet()) + { + // Get channel from local channel info + ColibriConferenceIQ.ChannelCommon rtpChanel + = getRtpChannel(localChannelsInfo, contentName); + if (rtpChanel == null) + { + // There's no channel for this content name in localChannelsInfo + continue; + } + + anyUpdates = true; + + // Ok we have channel for this content, let's add SSRCs + ColibriConferenceIQ.Channel reqChannel + = (ColibriConferenceIQ.Channel) getRequestChannel( + request.getOrCreateContent(contentName), rtpChanel); + + for (SourcePacketExtension ssrc : ssrcMap.get(contentName)) + { + reqChannel.addSource(ssrc.copy()); + } + + if (reqChannel.getSources() == null + || reqChannel.getSources().isEmpty()) + { + // Put an empty source to remove all sources + SourcePacketExtension emptySource = new SourcePacketExtension(); + emptySource.setSSRC(-1L); + reqChannel.addSource(emptySource); + } + } + + return anyUpdates; + } + + /** + * Adds next SSRC group information update request to + * {@link RequestType#CHANNEL_INFO_UPDATE} query currently being built. + * + * @param ssrcGroupMap the map of content name to the list of + * <tt>SourceGroupPacketExtension</tt>. + * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about + * Colibri channels to be updated. + * + * @return <tt>true</tt> if the request yields any changes in Colibri + * channels state on the bridge or <tt>false</tt> otherwise. + * In general when <tt>false</tt> is returned for all + * combined requests it makes no sense to send it. + */ + public boolean addSSSRCGroupsInfo( + Map<String, List<SourceGroupPacketExtension>> ssrcGroupMap, + ColibriConferenceIQ localChannelsInfo) + { + Objects.requireNonNull(ssrcGroupMap, "ssrcGroupMap"); + Objects.requireNonNull(localChannelsInfo, "localChannelsInfo"); + + if (conferenceState == null + || StringUtils.isNullOrEmpty(conferenceState.getID())) + { + // We are not initialized yet + return false; + } + + assertRequestType(RequestType.CHANNEL_INFO_UPDATE); + + request.setType(IQ.Type.SET); + + boolean anyUpdates = false; + + // Go over SSRC groups + for (String contentName : ssrcGroupMap.keySet()) + { + // Get channel from local channel info + ColibriConferenceIQ.Channel rtpChannel + = getRtpChannel(localChannelsInfo, contentName); + if (rtpChannel == null) + { + // There's no channel for this content name in localChannelsInfo + continue; + } + + List<SourceGroupPacketExtension> groups + = ssrcGroupMap.get(contentName); + + // Ok we have channel for this content, let's add SSRCs + ColibriConferenceIQ.Channel reqChannel + = (ColibriConferenceIQ.Channel) getRequestChannel( + request.getOrCreateContent(contentName), rtpChannel); + + if (groups.isEmpty() && "video".equalsIgnoreCase(contentName)) + { + anyUpdates = true; + + // Put empty source group to turn off simulcast layers + reqChannel.addSourceGroup( + SourceGroupPacketExtension.createSimulcastGroup()); + } + + for (SourceGroupPacketExtension group : groups) + { + anyUpdates = true; + + reqChannel.addSourceGroup(group); + } + } + + return anyUpdates; + } + + /** + * Adds next ICE transport update request to + * {@link RequestType#CHANNEL_INFO_UPDATE} query currently being built. + * + * @param map the map of content name to transport extensions. Maps + * transport to media types. + * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about + * Colibri channels to be updated. + * + * @return <tt>true</tt> if the request yields any changes in Colibri + * channels state on the bridge or <tt>false</tt> otherwise. + * In general when <tt>false</tt> is returned for all + * combined requests it makes no sense to send it. + */ + public boolean addTransportUpdateReq( + Map<String, IceUdpTransportPacketExtension> map, + ColibriConferenceIQ localChannelsInfo) + { + Objects.requireNonNull(map, "map"); + Objects.requireNonNull(localChannelsInfo, "localChannelsInfo"); + + if (conferenceState == null + || StringUtils.isNullOrEmpty(conferenceState.getID())) + { + // We are not initialized yet + return false; + } + + boolean hasAnyChanges = false; + + assertRequestType(RequestType.CHANNEL_INFO_UPDATE); + + request.setType(IQ.Type.SET); + + for (Map.Entry<String,IceUdpTransportPacketExtension> e + : map.entrySet()) + { + String contentName = e.getKey(); + ColibriConferenceIQ.ChannelCommon channel + = getColibriChannel(localChannelsInfo, contentName); + + if (channel != null) + { + IceUdpTransportPacketExtension transport + = IceUdpTransportPacketExtension + .cloneTransportAndCandidates(e.getValue(), true); + + ColibriConferenceIQ.ChannelCommon channelRequest + = channel instanceof ColibriConferenceIQ.Channel + ? new ColibriConferenceIQ.Channel() + : new ColibriConferenceIQ.SctpConnection(); + + channelRequest.setID(channel.getID()); + channelRequest.setEndpoint(channel.getEndpoint()); + channelRequest.setTransport(transport); + + request.getOrCreateContent(contentName) + .addChannelCommon(channelRequest); + + hasAnyChanges = true; + } + } + return hasAnyChanges; } /** @@ -583,10 +842,12 @@ public class ColibriBuilder request.setTo(videobridge); - if (requestType == RequestType.EXPIRE_CHANNELS - && !hasAnyChannelsToExpire) + if (requestType == RequestType.EXPIRE_CHANNELS) { - return null; + if (!hasAnyChannelsToExpire) + return null; + + hasAnyChannelsToExpire = false; } return request; @@ -720,6 +981,28 @@ public class ColibriBuilder } /** + * Returns an <tt>Integer</tt> which stands for the audio packet delay + * that will be set on all created audio channels or <tt>null</tt> if + * the builder should leave not include the XML attribute at all. + */ + public Integer getAudioPacketDelay() + { + return audioPacketDelay; + } + + /** + * Configures audio channels packet delay. + * @param audioPacketDelay an <tt>Integer</tt> value which stands for + * the audio packet delay that will be set on all created audio channels or + * <tt>null</tt> if the builder should not set that channel property to any + * value. + */ + public void setAudioPacketDelay(Integer audioPacketDelay) + { + this.audioPacketDelay = audioPacketDelay; + } + + /** * Sets channel 'simulcast-mode' option that will be added to the * request when channels are created. * @param simulcastMode a <tt>SimulcastMode</tt> value to specify @@ -732,69 +1015,64 @@ public class ColibriBuilder } /** - * Adds next payload type information update request to - * {@link RequestType#RTP_DESCRIPTION_UPDATE} query currently being built. + * Creates a new instance of <tt>localChannelInfo</tt> and initializes only + * the fields required to identify particular Colibri channel on the bridge. + * This instance is meant to be used in Colibri + * {@link RequestType#CHANNEL_INFO_UPDATE} requests. This instance is also + * added to given <tt>requestContent</tt> which used to construct current + * request. * - * @param map the map of content name to RTP description packet extension. - * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about - * Colibri channels to be updated. + * @param requestContent <tt>Content</tt> of Colibri update request to which + * new instance wil be automatically added after has been created. + * @param localChannelInfo the original channel for which "update request" + * equivalent is to be created with this call. * - * @return this instance for calls chaining purpose. + * @return new instance of <tt>localChannelInfo</tt> and initialized with + * only those fields required to identify particular Colibri channel on + * the bridge. */ - public ColibriBuilder addRtpDescription( - Map<String, RtpDescriptionPacketExtension> map, - ColibriConferenceIQ localChannelsInfo) { - - if (conferenceState == null - || StringUtils.isNullOrEmpty(conferenceState.getID())) - { - // We are not initialized yet - return null; - } - - assertRequestType(RequestType.RTP_DESCRIPTION_UPDATE); - - request.setType(IQ.Type.SET); - - for (Map.Entry<String, RtpDescriptionPacketExtension> e - : map.entrySet()) + private ColibriConferenceIQ.ChannelCommon getRequestChannel( + ColibriConferenceIQ.Content requestContent, + ColibriConferenceIQ.ChannelCommon localChannelInfo) + { + ColibriConferenceIQ.ChannelCommon reqChannel + = requestContent.getChannel(localChannelInfo.getID()); + if (reqChannel == null) { - String contentName = e.getKey(); - ColibriConferenceIQ.ChannelCommon channel - = getColibriChannel(localChannelsInfo, contentName); - - if (channel != null - && channel instanceof ColibriConferenceIQ.Channel) + if (localChannelInfo instanceof ColibriConferenceIQ.Channel) { - RtpDescriptionPacketExtension rtpPE = e.getValue(); - if (rtpPE == null) - { - continue; - } - - List<PayloadTypePacketExtension> pts = rtpPE.getPayloadTypes(); - if (pts == null || pts.isEmpty()) - { - continue; - } - - ColibriConferenceIQ.Channel channelRequest - = new ColibriConferenceIQ.Channel(); + reqChannel = new ColibriConferenceIQ.Channel(); + } + else if ( + localChannelInfo instanceof ColibriConferenceIQ.SctpConnection) + { + reqChannel = new ColibriConferenceIQ.SctpConnection(); + } + else + { + throw new RuntimeException( + "Unsupported ChannelCommon class: " + + localChannelInfo.getClass()); + } - channelRequest.setID(channel.getID()); + reqChannel.setID(localChannelInfo.getID()); - for (PayloadTypePacketExtension ptPE : rtpPE.getPayloadTypes()) - { - channelRequest.addPayloadType(ptPE); - } + requestContent.addChannelCommon(reqChannel); + } + return reqChannel; + } - request.getOrCreateContent(contentName) - .addChannel(channelRequest); - } + private ColibriConferenceIQ.Channel getRtpChannel( + ColibriConferenceIQ localChannelsInfo, + String contentName) + { + ColibriConferenceIQ.Content content + = localChannelsInfo.getContent(contentName); - } + if (content == null) + return null; - return this; + return content.getChannelCount() > 0 ? content.getChannel(0) : null; } /** @@ -808,19 +1086,10 @@ public class ColibriBuilder ALLOCATE_CHANNELS, /** - * Updates transport information for channels that use RTP bundle. - */ - BUNDLE_TRANSPORT_UPDATE, - - /** - * Updates channel transport information(ICE transport candidates). + * An update request which is meant to modify some values of existing + * Colibri channels on the bridge. */ - TRANSPORT_UPDATE, - - /** - * Updates the RTP description of a channel (payload types). - */ - RTP_DESCRIPTION_UPDATE, + CHANNEL_INFO_UPDATE, /** * Expires specified Colibri channels. @@ -833,4 +1102,28 @@ public class ColibriBuilder */ UNDEFINED; } + + /** + * Configures RTP-level relay (RFC 3550, section 2.3). + * @param rtpLevelRelayType an <tt>RTPLevelRelayType</tt> value which + * stands for the rtp level relay type that will be set on all created + * audio channels. + */ + public void setRTPLevelRelayType(RTPLevelRelayType rtpLevelRelayType) + { + this.rtpLevelRelayType = rtpLevelRelayType; + } + + /** + * Configures RTP-level relay (RFC 3550, section 2.3). + * @param rtpLevelRelayType a <tt>String</tt> value which + * stands for the rtp level relay type that will be set on all created + * audio channels. + */ + public void setRTPLevelRelayType(String rtpLevelRelayType) + { + setRTPLevelRelayType + (RTPLevelRelayType.parseRTPLevelRelayType(rtpLevelRelayType)); + } + } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriConferenceIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriConferenceIQ.java index 52368bf..d5cf175 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriConferenceIQ.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriConferenceIQ.java @@ -161,7 +161,7 @@ public class ColibriConferenceIQ throw new NullPointerException("channelBundle"); return - channelBundles.contains(channelBundles) + channelBundles.contains(channelBundle) ? false : channelBundles.add(channelBundle); } @@ -543,6 +543,14 @@ public class ColibriConferenceIQ = "receive-simulcast-layer"; /** + * The XML name of the <tt>packet-delay</tt> attribute of + * a <tt>channel</tt> of a <tt>content</tt> of a <tt>conference</tt> IQ + * which represents the value of the {@link #packetDelay} property of + * <tt>ColibriConferenceIQ.Channel</tt>. + */ + public static final String PACKET_DELAY_ATTR_NAME = "packet-delay"; + + /** * The XML name of the <tt>rtcpport</tt> attribute of a <tt>channel</tt> * of a <tt>content</tt> of a <tt>conference</tt> IQ which represents * the value of the <tt>rtcpPort</tt> property of @@ -613,6 +621,11 @@ public class ColibriConferenceIQ private SimulcastMode simulcastMode; /** + * The amount of delay added to the RTP stream in a number of packets. + */ + private Integer packetDelay; + + /** * The <tt>payload-type</tt> elements defined by XEP-0167: Jingle RTP * Sessions associated with this <tt>channel</tt>. */ @@ -893,6 +906,18 @@ public class ColibriConferenceIQ } /** + * Returns an <tt>Integer</tt> which stands for the amount of delay + * added to the RTP stream in a number of packets. + * + * @return <tt>Integer</tt> with the value or <tt>null</tt> if + * unspecified. + */ + public Integer getPacketDelay() + { + return packetDelay; + } + + /** * Gets a list of <tt>payload-type</tt> elements defined by XEP-0167: * Jingle RTP Sessions added to this <tt>channel</tt>. * @@ -1077,8 +1102,16 @@ public class ColibriConferenceIQ if (adaptiveSimulcast != null) { - xml.append(' ').append(adaptiveSimulcast).append("='") - .append(adaptiveSimulcast).append('\''); + xml.append(' ').append(ADAPTIVE_SIMULCAST_ATTR_NAME) + .append("='").append(adaptiveSimulcast).append('\''); + } + + // packet-delay + Integer packetDelay = getPacketDelay(); + if (packetDelay != null) + { + xml.append(' ').append(PACKET_DELAY_ATTR_NAME).append("='") + .append(packetDelay).append('\''); } // simulcastMode @@ -1314,6 +1347,17 @@ public class ColibriConferenceIQ } /** + * Configures channel's packet delay which tells by how many packets + * the RTP streams will be delayed. + * @param packetDelay an <tt>Integer</tt> value which stands for + * the packet delay that will be set or <tt>null</tt> to leave undefined + */ + public void setPacketDelay(Integer packetDelay) + { + this.packetDelay = packetDelay; + } + + /** * Sets the value of the 'simulcast-mode' flag. * @param simulcastMode the value to set. */ @@ -1925,6 +1969,25 @@ public class ColibriConferenceIQ } /** + * Adds <tt>ChannelCommon</tt> to this <tt>Content</tt>. + * @param channelCommon {@link ChannelCommon} instance to be added to + * this content. + * @return <tt>true</tt> if given <tt>channelCommon</tt> has been + * actually added to this <tt>Content</tt> instance. + */ + public boolean addChannelCommon(ChannelCommon channelCommon) + { + if (channelCommon instanceof Channel) + { + return addChannel((Channel) channelCommon); + } + else + { + return addSctpConnection((SctpConnection) channelCommon); + } + } + + /** * Adds a specific <tt>SctpConnection</tt> to the list of * <tt>SctpConnection</tt>s included into this <tt>Content</tt>. * diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriIQProvider.java index 39e299f..ce676ef 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriIQProvider.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriIQProvider.java @@ -20,6 +20,7 @@ package net.java.sip.communicator.impl.protocol.jabber.extensions.colibri; import net.java.sip.communicator.impl.protocol.jabber.extensions.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.service.protocol.jabber.*; import org.jitsi.service.neomedia.*; import org.jitsi.util.*; import org.jivesoftware.smack.packet.*; @@ -36,89 +37,101 @@ import org.xmlpull.v1.*; public class ColibriIQProvider implements IQProvider { + + /** + * Smack interoperation layer + */ + private AbstractSmackInteroperabilityLayer smackInteroperabilityLayer = + AbstractSmackInteroperabilityLayer.getInstance(); + /** Initializes a new <tt>ColibriIQProvider</tt> instance. */ public ColibriIQProvider() { - ProviderManager providerManager = ProviderManager.getInstance(); - - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( PayloadTypePacketExtension.ELEMENT_NAME, ColibriConferenceIQ.NAMESPACE, new DefaultPacketExtensionProvider<PayloadTypePacketExtension>( PayloadTypePacketExtension.class)); - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( RtcpFbPacketExtension.ELEMENT_NAME, RtcpFbPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<RtcpFbPacketExtension>( RtcpFbPacketExtension.class)); - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( RTPHdrExtPacketExtension.ELEMENT_NAME, ColibriConferenceIQ.NAMESPACE, new DefaultPacketExtensionProvider<RTPHdrExtPacketExtension>( RTPHdrExtPacketExtension.class)); - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( SourcePacketExtension.ELEMENT_NAME, SourcePacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<SourcePacketExtension>( SourcePacketExtension.class)); - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( SourceGroupPacketExtension.ELEMENT_NAME, SourceGroupPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<SourceGroupPacketExtension>( SourceGroupPacketExtension.class)); PacketExtensionProvider parameterProvider - = new DefaultPacketExtensionProvider<ParameterPacketExtension>( - ParameterPacketExtension.class); + = new DefaultPacketExtensionProvider<ParameterPacketExtension>( + ParameterPacketExtension.class); - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( ParameterPacketExtension.ELEMENT_NAME, ColibriConferenceIQ.NAMESPACE, parameterProvider); - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( ParameterPacketExtension.ELEMENT_NAME, SourcePacketExtension.NAMESPACE, parameterProvider); // Shutdown IQ - providerManager.addIQProvider( - GracefulShutdownIQ.ELEMENT_NAME, - GracefulShutdownIQ.NAMESPACE, + smackInteroperabilityLayer.addIQProvider( + ShutdownIQ.GRACEFUL_ELEMENT_NAME, + ShutdownIQ.NAMESPACE, + this); + smackInteroperabilityLayer.addIQProvider( + ShutdownIQ.FORCE_ELEMENT_NAME, + ShutdownIQ.NAMESPACE, this); // Shutdown extension PacketExtensionProvider shutdownProvider - = new DefaultPacketExtensionProvider - <ColibriConferenceIQ.GracefulShutdown>( - ColibriConferenceIQ.GracefulShutdown.class); + = new DefaultPacketExtensionProvider + <ColibriConferenceIQ.GracefulShutdown>( + ColibriConferenceIQ.GracefulShutdown.class); - providerManager.addExtensionProvider( - ColibriConferenceIQ.GracefulShutdown.ELEMENT_NAME, - ColibriConferenceIQ.GracefulShutdown.NAMESPACE, - shutdownProvider); + smackInteroperabilityLayer.addExtensionProvider( + ColibriConferenceIQ.GracefulShutdown.ELEMENT_NAME, + ColibriConferenceIQ.GracefulShutdown.NAMESPACE, + shutdownProvider); // ColibriStatsIQ - providerManager.addIQProvider( - ColibriStatsIQ.ELEMENT_NAME, - ColibriStatsIQ.NAMESPACE, - this); + smackInteroperabilityLayer.addIQProvider( + ColibriStatsIQ.ELEMENT_NAME, + ColibriStatsIQ.NAMESPACE, + this); // ColibriStatsExtension PacketExtensionProvider statsProvider - = new DefaultPacketExtensionProvider<ColibriStatsExtension>( - ColibriStatsExtension.class); + = new DefaultPacketExtensionProvider<ColibriStatsExtension>( + ColibriStatsExtension.class); - providerManager.addExtensionProvider( - ColibriStatsExtension.ELEMENT_NAME, - ColibriStatsExtension.NAMESPACE, - statsProvider); + smackInteroperabilityLayer.addExtensionProvider( + ColibriStatsExtension.ELEMENT_NAME, + ColibriStatsExtension.NAMESPACE, + statsProvider); // ColibriStatsExtension.Stat PacketExtensionProvider statProvider - = new DefaultPacketExtensionProvider<ColibriStatsExtension.Stat>( - ColibriStatsExtension.Stat.class); - - providerManager.addExtensionProvider( - ColibriStatsExtension.Stat.ELEMENT_NAME, - ColibriStatsExtension.NAMESPACE, - statProvider); + = new DefaultPacketExtensionProvider + <ColibriStatsExtension.Stat>( + ColibriStatsExtension.Stat.class); + + smackInteroperabilityLayer.addExtensionProvider( + ColibriStatsExtension.Stat.ELEMENT_NAME, + ColibriStatsExtension.NAMESPACE, + statProvider); + + } private void addChildExtension( @@ -199,8 +212,7 @@ public class ColibriIQProvider throws Exception { PacketExtensionProvider extensionProvider - = (PacketExtensionProvider) - ProviderManager.getInstance().getExtensionProvider( + = smackInteroperabilityLayer.getExtensionProvider( name, namespace); PacketExtension extension; @@ -416,6 +428,15 @@ public class ColibriIQProvider if ((expire != null) && (expire.length() != 0)) channel.setExpire(Integer.parseInt(expire)); + String packetDelay + = parser.getAttributeValue( + "", + ColibriConferenceIQ.Channel + .PACKET_DELAY_ATTR_NAME); + if (!StringUtils.isNullOrEmpty(packetDelay)) + channel.setPacketDelay( + Integer.parseInt(packetDelay)); + // host String host = parser.getAttributeValue( @@ -464,6 +485,18 @@ public class ColibriIQProvider channel.setAdaptiveLastN( Boolean.parseBoolean(adaptiveLastN)); + String adaptiveSimulcast + = parser.getAttributeValue( + "", + ColibriConferenceIQ.Channel + .ADAPTIVE_SIMULCAST_ATTR_NAME); + + if (!StringUtils.isNullOrEmpty(adaptiveSimulcast)) + { + channel.setAdaptiveSimulcast( + Boolean.parseBoolean(adaptiveSimulcast)); + } + // simulcastMode String simulcastMode = parser.getAttributeValue( @@ -802,12 +835,12 @@ public class ColibriIQProvider iq = conference; } - else if (GracefulShutdownIQ.ELEMENT_NAME.equals(parser.getName()) - && GracefulShutdownIQ.NAMESPACE.equals(namespace)) + else if (ShutdownIQ.NAMESPACE.equals(namespace) && + ShutdownIQ.isValidElementName(parser.getName())) { String rootElement = parser.getName(); - iq = new GracefulShutdownIQ(); + iq = ShutdownIQ.createShutdownIQ(rootElement); boolean done = false; @@ -825,12 +858,6 @@ public class ColibriIQProvider } break; } - - case XmlPullParser.TEXT: - { - // Parse some text here - break; - } } } } @@ -891,12 +918,6 @@ public class ColibriIQProvider } break; } - - case XmlPullParser.TEXT: - { - // Parse some text here - break; - } } } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriStreamConnector.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriStreamConnector.java index d5a6ce1..ef80392 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriStreamConnector.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriStreamConnector.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,74 +15,74 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.jabber.extensions.colibri;
-
-import org.jitsi.service.neomedia.*;
-
-/**
- * Implements a <tt>StreamConnector</tt> which allows sharing a specific
- * <tt>StreamConnector</tt> instance among multiple <tt>TransportManager</tt>s
- * for the purposes of the Jitsi Videobridge.
- *
- * @author Lyubomir Marinov
- */
-public class ColibriStreamConnector
- extends StreamConnectorDelegate<StreamConnector>
-{
- /**
- * Initializes a new <tt>ColibriStreamConnector</tt> instance which is to
- * share a specific <tt>StreamConnector</tt> instance among multiple
- * <tt>TransportManager</tt>s for the purposes of the Jitsi Videobridge.
- *
- * @param streamConnector the <tt>StreamConnector</tt> instance to be shared
- * by the new instance among multiple <tt>TransportManager</tt>s for the
- * purposes of the Jitsi Videobridge
- */
- public ColibriStreamConnector(StreamConnector streamConnector)
- {
- super(streamConnector);
- }
-
- /**
- * {@inheritDoc}
- *
- * Overrides {@link StreamConnectorDelegate#close()} in order to prevent the
- * closing of the <tt>StreamConnector</tt> wrapped by this instance because
- * the latter is shared and it is not clear whether no
- * <tt>TransportManager</tt> is using it.
- */
- @Override
- public void close()
- {
- /*
- * Do not close the shared StreamConnector because it is not clear
- * whether no TransportManager is using it.
- */
- }
-
- /**
- * {@inheritDoc}
- *
- * Invokes {@link #close()} on this instance when it is clear that no
- * <tt>TransportManager</tt> is using it in order to release the resources
- * allocated by this instance throughout its life time (that need explicit
- * disposal).
- */
- @Override
- protected void finalize()
- throws Throwable
- {
- try
- {
- /*
- * Close the shared StreamConnector because it is clear that no
- * TrasportManager is using it.
- */
- super.close();
- }
- finally
- {
- super.finalize();
- }
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber.extensions.colibri; + +import org.jitsi.service.neomedia.*; + +/** + * Implements a <tt>StreamConnector</tt> which allows sharing a specific + * <tt>StreamConnector</tt> instance among multiple <tt>TransportManager</tt>s + * for the purposes of the Jitsi Videobridge. + * + * @author Lyubomir Marinov + */ +public class ColibriStreamConnector + extends StreamConnectorDelegate<StreamConnector> +{ + /** + * Initializes a new <tt>ColibriStreamConnector</tt> instance which is to + * share a specific <tt>StreamConnector</tt> instance among multiple + * <tt>TransportManager</tt>s for the purposes of the Jitsi Videobridge. + * + * @param streamConnector the <tt>StreamConnector</tt> instance to be shared + * by the new instance among multiple <tt>TransportManager</tt>s for the + * purposes of the Jitsi Videobridge + */ + public ColibriStreamConnector(StreamConnector streamConnector) + { + super(streamConnector); + } + + /** + * {@inheritDoc} + * + * Overrides {@link StreamConnectorDelegate#close()} in order to prevent the + * closing of the <tt>StreamConnector</tt> wrapped by this instance because + * the latter is shared and it is not clear whether no + * <tt>TransportManager</tt> is using it. + */ + @Override + public void close() + { + /* + * Do not close the shared StreamConnector because it is not clear + * whether no TransportManager is using it. + */ + } + + /** + * {@inheritDoc} + * + * Invokes {@link #close()} on this instance when it is clear that no + * <tt>TransportManager</tt> is using it in order to release the resources + * allocated by this instance throughout its life time (that need explicit + * disposal). + */ + @Override + protected void finalize() + throws Throwable + { + try + { + /* + * Close the shared StreamConnector because it is clear that no + * TrasportManager is using it. + */ + super.close(); + } + finally + { + super.finalize(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ShutdownIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ShutdownIQ.java new file mode 100644 index 0000000..4df251b --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ShutdownIQ.java @@ -0,0 +1,134 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.colibri; + +import org.jivesoftware.smack.packet.*; + +/** + * The IQ used to trigger the graceful shutdown mode of the videobridge or force + * shutdown the one which receives the stanza(given that source JID is + * authorized to do so). + * + * @author Pawel Domas + */ +public class ShutdownIQ + extends IQ +{ + /** + * XML namespace name for shutdown IQs. + */ + final static public String NAMESPACE = ColibriConferenceIQ.NAMESPACE; + + /** + * Force shutdown IQ element name. + */ + final static public String FORCE_ELEMENT_NAME = "force-shutdown"; + + /** + * Graceful shutdown IQ element name. + */ + final static public String GRACEFUL_ELEMENT_NAME = "graceful-shutdown"; + + /** + * The element name of this IQ. Either {@link #FORCE_ELEMENT_NAME} or + * {@link #GRACEFUL_ELEMENT_NAME}. + */ + private final String elementName; + + /** + * Checks if given element is a valid one for <tt>ShutdownIQ</tt>. + * + * @param elementName the name if XML element name inside of the IQ. + * + * @return <tt>true</tt> if given <tt>elementName</tt> is correct for + * <tt>ShutdownIQ</tt>. + */ + public static boolean isValidElementName(String elementName) + { + return GRACEFUL_ELEMENT_NAME.equals(elementName) + || FORCE_ELEMENT_NAME.equals(elementName); + } + + /** + * Creates shutdown IQ for given element name. + * + * @param elementName can be {@link #FORCE_ELEMENT_NAME} or + * {@link #GRACEFUL_ELEMENT_NAME} + * + * @return new <tt>ShutdownIQ</tt> instance for given element name. + * + * @throws IllegalArgumentException if given element name is neither + * {@link #FORCE_ELEMENT_NAME} nor {@link #GRACEFUL_ELEMENT_NAME}. + */ + public static ShutdownIQ createShutdownIQ(String elementName) + { + if (!isValidElementName(elementName)) + { + throw new IllegalArgumentException( + "Invalid element name: " + elementName); + } + + if (GRACEFUL_ELEMENT_NAME.equals(elementName)) + { + return createGracefulShutdownIQ(); + } + else + { + return createForceShutdownIQ(); + } + } + + /** + * Creates and returns new instance of graceful shutdown IQ. + */ + public static ShutdownIQ createGracefulShutdownIQ() + { + return new ShutdownIQ(GRACEFUL_ELEMENT_NAME); + } + + /** + * Creates and returns new instance of force shutdown IQ. + */ + public static ShutdownIQ createForceShutdownIQ() + { + return new ShutdownIQ(FORCE_ELEMENT_NAME); + } + + private ShutdownIQ(String elementName) + { + this.elementName = elementName; + } + + /** + * Returns <tt>true</tt> if this IQ instance is a "graceful shutdown" one. + * Otherwise it is a force shutdown IQ. + */ + public boolean isGracefulShutdown() + { + return elementName.equals(GRACEFUL_ELEMENT_NAME); + } + + /** + * {@inheritDoc} + */ + @Override + public String getChildElementXML() + { + return "<" + elementName + " xmlns='" + NAMESPACE + "' />"; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/GracefulShutdownIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQ.java index 9808b11..661bb1b 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/GracefulShutdownIQ.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQ.java @@ -15,23 +15,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.jabber.extensions.colibri; +package net.java.sip.communicator.impl.protocol.jabber.extensions.health; import org.jivesoftware.smack.packet.*; /** - * The IQ used to trigger the graceful shutdown mode of the videobridge which - * receives the stanza(given that source JID is authorized to start it). + * The health check IQ used to trigger health checks on the Jitsi Videobridge. * * @author Pawel Domas */ -public class GracefulShutdownIQ +public class HealthCheckIQ extends IQ { - public static final String NAMESPACE = ColibriConferenceIQ.NAMESPACE; + /** + * Health check IQ element name. + */ + final static public String ELEMENT_NAME = "healthcheck"; - public static final String ELEMENT_NAME = "graceful-shutdown"; + /** + * XML namespace name for health check IQs. + */ + final static public String NAMESPACE + = "http://jitsi.org/protocol/healthcheck"; + /** + * {@inheritDoc} + */ @Override public String getChildElementXML() { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQProvider.java new file mode 100644 index 0000000..9c2903d --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQProvider.java @@ -0,0 +1,94 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.health; + +import net.java.sip.communicator.service.protocol.jabber.*; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; + +import org.xmlpull.v1.*; + +/** + * The <tt>IQProvider</tt> for {@link HealthCheckIQ}. + * + * @author Pawel Domas + */ +public class HealthCheckIQProvider + implements IQProvider +{ + /** + * Registers <tt>HealthCheckIQProvider</tt> as an <tt>IQProvider</tt> + * in {@link AbstractSmackInteroperabilityLayer}. + */ + public static void registerIQProvider() + { + AbstractSmackInteroperabilityLayer smackInteropLayer = + AbstractSmackInteroperabilityLayer.getInstance(); + + // ColibriStatsIQ + smackInteropLayer.addIQProvider( + HealthCheckIQ.ELEMENT_NAME, + HealthCheckIQ.NAMESPACE, + new HealthCheckIQProvider()); + } + + /** + * Parses <tt>HealthCheckIQ</tt>. + * + * {@inheritDoc} + */ + @Override + public IQ parseIQ(XmlPullParser parser) + throws Exception + { + String namespace = parser.getNamespace(); + IQ iq; + + if (HealthCheckIQ.ELEMENT_NAME.equals(parser.getName()) + && HealthCheckIQ.NAMESPACE.equals(namespace)) + { + String rootElement = parser.getName(); + + iq = new HealthCheckIQ(); + + boolean done = false; + + while (!done) + { + switch (parser.next()) + { + case XmlPullParser.END_TAG: + { + String name = parser.getName(); + + if (rootElement.equals(name)) + { + done = true; + } + break; + } + } + } + } + else + iq = null; + + return iq; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIq.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIq.java new file mode 100644 index 0000000..8b964af --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIq.java @@ -0,0 +1,413 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jibri; + +import org.jitsi.util.*; + +import org.jivesoftware.smack.packet.*; + +import java.util.*; + +/** + * The IQ used to control conference recording with Jibri component. + * + * Start the recording: + * + * 1. Send Jibri IQ with {@link Action#START} to Jibri. + * 2. Jibri replies with RESULT and status {@link Status#PENDING}. + * 3. Jibri sends SET IQ with status {@link Status#ON} once recording actually + * starts. + * + * Stop the recording: + * + * 1. Send Jibri IQ with {@link Action#STOP} to Jibri. + * 2. Jibri replies with {@link Status#OFF} immediately if the recording has + * been stopped already or sends separate Jibri SET IQ later on if it takes + * more time. + * + * @author lishunyang + * @author Pawel Domas + */ +public class JibriIq + extends IQ +{ + /** + * Attribute name of "action". + */ + public static final String ACTION_ATTR_NAME = "action"; + + /** + * XML element name of the Jibri IQ. + */ + public static final String ELEMENT_NAME = "jibri"; + + /** + * XML namespace of the Jibri IQ. + */ + public static final String NAMESPACE = "http://jitsi.org/protocol/jibri"; + + /** + * The name of XML attribute which stores the recording status. + */ + static final String STATUS_ATTR_NAME = "status"; + + /** + * The name of XML attribute which stores the stream id. + */ + static final String STREAM_ID_ATTR_NAME = "streamid"; + + /** + * The name of XML attribute which stores the name of the conference room to + * be recorded. + */ + static final String ROOM_ATTR_NAME = "room"; + + /** + * Holds the action. + */ + private Action action = Action.UNDEFINED; + + /** + * XMPPError stores error details for {@link Status#FAILED}. + */ + private XMPPError error; + + /** + * Holds recording status. + */ + private Status status = Status.UNDEFINED; + + /** + * The ID of the stream which will be used to record the conference. The + * value depends on recording service provider. + */ + private String streamId = null; + + /** + * The name of the conference room to be recorded. + */ + private String room = null; + + /** + * Returns the value of {@link #STREAM_ID_ATTR_NAME} attribute. + * @return a <tt>String</tt> which contains the value of "stream id" + * attribute or <tt>null</tt> if empty. + */ + public String getStreamId() + { + return streamId; + } + + /** + * Sets the value for {@link #STREAM_ID_ATTR_NAME} attribute. + * @param streamId a <tt>String</tt> for the stream id attribute or + * <tt>null</tt> to remove it from XML element. + */ + public void setStreamId(String streamId) + { + this.streamId = streamId; + } + + /** + * Returns the value of {@link #ROOM_ATTR_NAME} attribute. + * @return a <tt>String</tt> which contains the value of the room attribute + * or <tt>null</tt> if empty. + * @see #room + */ + public String getRoom() + { + return room; + } + + /** + * Sets the value for {@link #ROOM_ATTR_NAME} attribute. + * @param room a <tt>String</tt> for the room attribute or <tt>null</tt> to + * remove it from XML element. + * @see #room + */ + public void setRoom(String room) + { + this.room = room; + } + + /** + * {@inheritDoc} + */ + @Override + public String getChildElementXML() + { + StringBuilder xml = new StringBuilder(); + + xml.append('<').append(ELEMENT_NAME); + xml.append(" xmlns='").append(NAMESPACE).append("' "); + + if (action != Action.UNDEFINED) + { + printStringAttribute(xml, ACTION_ATTR_NAME, action.toString()); + } + + if (status != Status.UNDEFINED) + { + printStringAttribute(xml, STATUS_ATTR_NAME, status.toString()); + } + + if (room != null) + { + printStringAttribute(xml, ROOM_ATTR_NAME, room); + } + + if (streamId != null) + { + printStringAttribute(xml, STREAM_ID_ATTR_NAME, streamId); + } + + Collection<PacketExtension> extensions = getExtensions(); + if (extensions.size() > 0) + { + xml.append(">"); + for (PacketExtension extension : extensions) + { + xml.append(extension.toXML()); + } + xml.append("</").append(ELEMENT_NAME).append(">"); + } + else + { + xml.append("/>"); + } + + return xml.toString(); + } + + private void printStringAttribute( + StringBuilder xml, String attrName, String attr) + { + if (!StringUtils.isNullOrEmpty(attr)) + { + attr = org.jivesoftware.smack.util.StringUtils.escapeForXML(attr); + xml.append(attrName).append("='") + .append(attr).append("' "); + } + } + + /** + * Sets the value of 'action' attribute. + * + * @param action the value to be set as 'action' attribute of this IQ. + */ + public void setAction(Action action) + { + this.action = action; + } + + /** + * Returns the value of 'action' attribute. + */ + public Action getAction() + { + return action; + } + + /** + * Sets the value of 'status' attribute. + */ + public void setStatus(Status status) + { + this.status = status; + } + + /** + * Returns the value of 'status' attribute. + */ + public Status getStatus() + { + return status; + } + + /** + * Sets the <tt>XMPPError</tt> which will provide details about Jibri + * failure. It is expected to be set when this IQ's status value is + * {@link Status#FAILED}. + * + * @param error <tt>XMPPError</tt> to be set on this <tt>JibriIq</tt> + * instance. + */ + public void setXMPPError(XMPPError error) + { + this.error = error; + } + + /** + * Returns {@link XMPPError} with Jibri error details when the status is + * {@link Status#FAILED}. + */ + public XMPPError getError() + { + return error; + } + + /** + * Enumerative value of attribute "action" in recording extension. + * + * @author lishunyang + * @author Pawel Domas + * + */ + public enum Action + { + /** + * Start the recording. + */ + START("start"), + /** + * Stop the recording. + */ + STOP("stop"), + /** + * Unknown/uninitialized + */ + UNDEFINED("undefined"); + + private String name; + + Action(String name) + { + this.name = name; + } + + @Override + public String toString() + { + return name; + } + + /** + * Parses <tt>Action</tt> from given string. + * + * @param action the string representation of <tt>Action</tt>. + * + * @return <tt>Action</tt> value for given string or + * {@link #UNDEFINED} if given string does not + * reflect any of valid values. + */ + public static Action parse(String action) + { + if (StringUtils.isNullOrEmpty(action)) + return UNDEFINED; + + try + { + return Action.valueOf(action.toUpperCase()); + } + catch(IllegalArgumentException e) + { + return UNDEFINED; + } + } + } + + /** + * The enumeration of recording status values. + */ + public enum Status + { + /** + * Recording is in progress. + */ + ON("on"), + + /** + * Recording stopped. + */ + OFF("off"), + + /** + * Starting the recording process. + */ + PENDING("pending"), + + /** + * The recorder has failed and the service is retrying on another + * instance. + */ + RETRYING("retrying"), + + /** + * An error occurred any point during startup, recording or shutdown. + */ + FAILED("failed"), + + /** + * There are Jibri instances connected to the system, but all of them + * are currently busy. + */ + BUSY("busy"), + + /** + * Unknown/uninitialized. + */ + UNDEFINED("undefined"); + + /** + * Status name holder. + */ + private String name; + + /** + * Creates new {@link Status} instance. + * @param name a string corresponding to one of {@link Status} values. + */ + Status(String name) + { + this.name = name; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + return name; + } + + /** + * Parses <tt>Status</tt> from given string. + * + * @param status the string representation of <tt>Status</tt>. + * + * @return <tt>Status</tt> value for given string or + * {@link #UNDEFINED} if given string does not + * reflect any of valid values. + */ + public static Status parse(String status) + { + if (StringUtils.isNullOrEmpty(status)) + return UNDEFINED; + + try + { + return Status.valueOf(status.toUpperCase()); + } + catch(IllegalArgumentException e) + { + return UNDEFINED; + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIqProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIqProvider.java new file mode 100644 index 0000000..155853c --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIqProvider.java @@ -0,0 +1,112 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jibri; + +import org.jitsi.util.*; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; +import org.jivesoftware.smack.util.PacketParserUtils; + +import org.xmlpull.v1.*; + +/** + * Parses {@link JibriIq}. + */ +public class JibriIqProvider + implements IQProvider +{ + /** + * {@inheritDoc} + */ + @Override + public IQ parseIQ(XmlPullParser parser) + throws Exception + { + String namespace = parser.getNamespace(); + + // Check the namespace + if (!JibriIq.NAMESPACE.equals(namespace)) + { + return null; + } + + String rootElement = parser.getName(); + + JibriIq iq; + + if (JibriIq.ELEMENT_NAME.equals(rootElement)) + { + iq = new JibriIq(); + + String action + = parser.getAttributeValue("", JibriIq.ACTION_ATTR_NAME); + iq.setAction(JibriIq.Action.parse(action)); + + String status + = parser.getAttributeValue("", JibriIq.STATUS_ATTR_NAME); + iq.setStatus(JibriIq.Status.parse(status)); + + String room + = parser.getAttributeValue("", JibriIq.ROOM_ATTR_NAME); + if (!StringUtils.isNullOrEmpty(room)) + iq.setRoom(room); + + String streamId + = parser.getAttributeValue("", JibriIq.STREAM_ID_ATTR_NAME); + if (!StringUtils.isNullOrEmpty(streamId)) + iq.setStreamId(streamId); + } + else + { + return null; + } + + boolean done = false; + + while (!done) + { + switch (parser.next()) + { + case XmlPullParser.START_TAG: + { + String name = parser.getName(); + + if ("error".equals(name)) + { + XMPPError error = PacketParserUtils.parseError(parser); + iq.setXMPPError(error); + } + break; + } + case XmlPullParser.END_TAG: + { + String name = parser.getName(); + + if (rootElement.equals(name)) + { + done = true; + } + break; + } + } + } + + return iq; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriStatusPacketExt.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriStatusPacketExt.java new file mode 100644 index 0000000..e046b68 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriStatusPacketExt.java @@ -0,0 +1,121 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jibri; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +import org.jitsi.util.*; + +import org.jivesoftware.smack.provider.*; + +/** + * Status extension included in MUC presence by Jibri to indicate it's status. + * One of: + * <li>idle</li> - the instance is idle and can be used for recording + * <li>busy</li> - the instance is currently recording or doing something very + * important and should not be disturbed + * + * + */ +public class JibriStatusPacketExt + extends AbstractPacketExtension +{ + /** + * The namespace of this packet extension. + */ + public static final String NAMESPACE = JibriIq.NAMESPACE; + + /** + * XML element name of this packet extension. + */ + public static final String ELEMENT_NAME = "jibri-status"; + + private static final String STATUS_ATTRIBUTE = "status"; + + /** + * Creates new instance of <tt>VideoMutedExtension</tt>. + */ + public JibriStatusPacketExt() + { + super(NAMESPACE, ELEMENT_NAME); + } + + static public void registerExtensionProvider() + { + ProviderManager.getInstance().addExtensionProvider( + ELEMENT_NAME, + NAMESPACE, + new DefaultPacketExtensionProvider<JibriStatusPacketExt>( + JibriStatusPacketExt.class) + ); + } + + public Status getStatus() + { + return Status.parse(getAttributeAsString(STATUS_ATTRIBUTE)); + } + + public void setStatus(Status status) + { + setAttribute(STATUS_ATTRIBUTE, String.valueOf(status)); + } + + public enum Status + { + IDLE("idle"), + BUSY("busy"), + UNDEFINED("undefined"); + + private String name; + + Status(String name) + { + this.name = name; + } + + @Override + public String toString() + { + return name; + } + + /** + * Parses <tt>Status</tt> from given string. + * + * @param status the string representation of <tt>Status</tt>. + * + * @return <tt>Status</tt> value for given string or + * {@link #UNDEFINED} if given string does not + * reflect any of valid values. + */ + public static Status parse(String status) + { + if (StringUtils.isNullOrEmpty(status)) + return UNDEFINED; + + try + { + return Status.valueOf(status.toUpperCase()); + } + catch(IllegalArgumentException e) + { + return UNDEFINED; + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/RecordingStatus.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/RecordingStatus.java new file mode 100644 index 0000000..13177cf --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/RecordingStatus.java @@ -0,0 +1,126 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jibri; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +import org.jivesoftware.smack.packet.*; + +import java.util.*; + +/** + * The packet extension added to Jicofo MUC presence to broadcast current + * recording status to all conference participants. + * + * Status meaning: + * <tt>{@link JibriIq.Status#UNDEFINED}</tt> - recording not available + * <tt>{@link JibriIq.Status#OFF}</tt> - recording stopped(available to start) + * <tt>{@link JibriIq.Status#PENDING}</tt> - starting recording + * <tt>{@link JibriIq.Status#ON}</tt> - recording in progress + */ +public class RecordingStatus + extends AbstractPacketExtension +{ + /** + * The namespace of this packet extension. + */ + public static final String NAMESPACE = JibriIq.NAMESPACE; + + /** + * XML element name of this packet extension. + */ + public static final String ELEMENT_NAME = "jibri-recording-status"; + + /** + * The name of XML attribute which holds the recording status. + */ + private static final String STATUS_ATTRIBUTE = "status"; + + public RecordingStatus() + { + super(NAMESPACE, ELEMENT_NAME); + } + + /** + * Returns the value of current recording status stored in it's attribute. + * @return one of {@link JibriIq.Status} + */ + public JibriIq.Status getStatus() + { + String statusAttr = getAttributeAsString(STATUS_ATTRIBUTE); + + return JibriIq.Status.parse(statusAttr); + } + + /** + * Sets new value for the recording status. + * @param status one of {@link JibriIq.Status} + */ + public void setStatus(JibriIq.Status status) + { + setAttribute(STATUS_ATTRIBUTE, String.valueOf(status)); + } + + /** + * Returns <tt>XMPPError</tt> associated with current + * {@link RecordingStatus}. + */ + public XMPPError getError() + { + XMPPErrorPE errorPe = getErrorPE(); + return errorPe != null ? errorPe.getError() : null; + } + + /** + * Gets <tt>{@link XMPPErrorPE}</tt> from the list of child packet + * extensions. + * @return {@link XMPPErrorPE} or <tt>null</tt> if not found. + */ + private XMPPErrorPE getErrorPE() + { + List<? extends PacketExtension> errorPe + = getChildExtensionsOfType(XMPPErrorPE.class); + + return (XMPPErrorPE) (!errorPe.isEmpty() ? errorPe.get(0) : null); + } + + /** + * Sets <tt>XMPPError</tt> on this <tt>RecordingStatus</tt>. + * @param error <tt>XMPPError</tt> to add error details to this + * <tt>RecordingStatus</tt> instance or <tt>null</tt> to have it removed. + */ + public void setError(XMPPError error) + { + if (error != null) + { + // Wrap and add XMPPError as packet extension + XMPPErrorPE errorPe = getErrorPE(); + if (errorPe == null) + { + errorPe = new XMPPErrorPE(error); + addChildExtension(errorPe); + } + errorPe.setError(error); + } + else + { + // Remove error PE + getChildExtensions().remove(getErrorPE()); + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/XMPPErrorPE.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/XMPPErrorPE.java new file mode 100644 index 0000000..a72f310 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/XMPPErrorPE.java @@ -0,0 +1,93 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jibri; + +import org.jivesoftware.smack.packet.*; + +import java.util.*; + +/** + * Wraps Smack's <tt>XMPPError</tt> into <tt>PacketExtension</tt>, so that it + * can be easily inserted into {@link RecordingStatus}. + */ +public class XMPPErrorPE + implements PacketExtension +{ + /** + * <tt>XMPPError</tt> wrapped into this <tt>XMPPErrorPE</tt>. + */ + private XMPPError error; + + /** + * Creates new instance of <tt>XMPPErrorPE</tt>. + * @param xmppError the instance of <tt>XMPPError</tt> that will be wrapped + * by the newly created <tt>XMPPErrorPE</tt>. + */ + public XMPPErrorPE(XMPPError xmppError) + { + setError(xmppError); + } + + /** + * Returns the underlying instance of <tt>XMPPError</tt>. + */ + public XMPPError getError() + { + return error; + } + + /** + * Sets new instance of <tt>XMPPError</tt> to be wrapped by this + * <tt>XMPPErrorPE</tt>. + * @param error <tt>XMPPError</tt> that will be wrapped by this + * <TT>XMPPErrorPE</TT>. + */ + public void setError(XMPPError error) + { + Objects.requireNonNull(error, "error"); + + this.error = error; + } + + /** + * {@inheritDoc} + */ + @Override + public String getElementName() + { + return "error"; + } + + /** + * {@inheritDoc} + */ + @Override + public String getNamespace() + { + return ""; + } + + /** + * {@inheritDoc} + */ + @Override + public String toXML() + { + return error.toXML(); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CandidatePacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CandidatePacketExtension.java index 0c5b190..46b1e77 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CandidatePacketExtension.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CandidatePacketExtension.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,441 +15,441 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
-import org.ice4j.ice.*;
-
-/**
- * @author Emil Ivov
- */
-public class CandidatePacketExtension extends AbstractPacketExtension
- implements Comparable<CandidatePacketExtension>
-{
- /**
- * The name of the "candidate" element.
- */
- public static final String ELEMENT_NAME = "candidate";
-
- /**
- * The name of the "component" element.
- */
- public static final String COMPONENT_ATTR_NAME = "component";
-
- /**
- * The "component" ID for RTP components.
- */
- public static final int RTP_COMPONENT_ID = 1;
-
- /**
- * The "component" ID for RTCP components.
- */
- public static final int RTCP_COMPONENT_ID = 2;
-
- /**
- * The name of the "foundation" element.
- */
- public static final String FOUNDATION_ATTR_NAME = "foundation";
-
- /**
- * The name of the "generation" element.
- */
- public static final String GENERATION_ATTR_NAME = "generation";
-
- /**
- * The name of the "id" element.
- */
- public static final String ID_ATTR_NAME = "id";
-
- /**
- * The name of the "ip" element.
- */
- public static final String IP_ATTR_NAME = "ip";
-
- /**
- * The name of the "network" element.
- */
- public static final String NETWORK_ATTR_NAME = "network";
-
- /**
- * The name of the "port" element.
- */
- public static final String PORT_ATTR_NAME = "port";
-
- /**
- * The name of the "priority" element.
- */
- public static final String PRIORITY_ATTR_NAME = "priority";
-
- /**
- * The name of the "protocol" element.
- */
- public static final String PROTOCOL_ATTR_NAME = "protocol";
-
- /**
- * The name of the "rel-addr" element.
- */
- public static final String REL_ADDR_ATTR_NAME = "rel-addr";
-
- /**
- * The name of the "rel-port" element.
- */
- public static final String REL_PORT_ATTR_NAME = "rel-port";
-
- /**
- * The name of the "type" element.
- */
- public static final String TYPE_ATTR_NAME = "type";
-
- /**
- * The name of the "tcptype" element.
- */
- public static final String TCPTYPE_ATTR_NAME = "tcptype";
-
- /**
- * Creates a new {@link CandidatePacketExtension}
- */
- public CandidatePacketExtension()
- {
- super(null, ELEMENT_NAME);
- }
-
- /**
- * Creates a new {@link CandidatePacketExtension} with the specified
- * <tt>elementName</tt> so that this class would be usable as a
- * <tt>RemoteCandidatePacketExtension</tt> parent.
- *
- * @param elementName the element name that this instance should be using.
- */
- protected CandidatePacketExtension(String elementName)
- {
- super(null, elementName);
- }
-
- /**
- * Sets a component ID as defined in ICE-CORE.
- *
- * @param component a component ID as defined in ICE-CORE.
- */
- public void setComponent(int component)
- {
- super.setAttribute(COMPONENT_ATTR_NAME, component);
- }
-
- /**
- * Returns a component ID as defined in ICE-CORE.
- *
- * @return a component ID as defined in ICE-CORE.
- */
- public int getComponent()
- {
- return super.getAttributeAsInt(COMPONENT_ATTR_NAME);
- }
-
- /**
- * Sets the candidate foundation as defined in ICE-CORE.
- *
- * @param foundation the candidate foundation as defined in ICE-CORE.
- */
- public void setFoundation(String foundation)
- {
- super.setAttribute(FOUNDATION_ATTR_NAME, foundation);
- }
-
- /**
- * Returns the candidate foundation as defined in ICE-CORE.
- *
- * @return the candidate foundation as defined in ICE-CORE.
- */
- public String getFoundation()
- {
- return super.getAttributeAsString(FOUNDATION_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's generation index. A generation is an index,
- * starting at 0, that enables the parties to keep track of updates to the
- * candidate throughout the life of the session. For details, see the ICE
- * Restarts section of XEP-0176.
- *
- * @param generation this candidate's generation index.
- */
- public void setGeneration(int generation)
- {
- super.setAttribute(GENERATION_ATTR_NAME, generation);
- }
-
- /**
- * Returns this candidate's generation. A generation is an index, starting at
- * 0, that enables the parties to keep track of updates to the candidate
- * throughout the life of the session. For details, see the ICE Restarts
- * section of XEP-0176.
- *
- * @return this candidate's generation index.
- */
- public int getGeneration()
- {
- return super.getAttributeAsInt(GENERATION_ATTR_NAME);
- }
-
- /**
- * Sets this candidates's unique identifier <tt>String</tt>.
- *
- * @param id this candidates's unique identifier <tt>String</tt>
- */
- public void setID(String id)
- {
- super.setAttribute(ID_ATTR_NAME, id);
- }
-
- /**
- * Returns this candidates's unique identifier <tt>String</tt>.
- *
- * @return this candidates's unique identifier <tt>String</tt>
- */
- public String getID()
- {
- return super.getAttributeAsString(ID_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's Internet Protocol (IP) address; this can be either
- * an IPv4 address or an IPv6 address.
- *
- * @param ip this candidate's IPv4 or IPv6 address.
- */
- public void setIP(String ip)
- {
- super.setAttribute(IP_ATTR_NAME, ip);
- }
-
- /**
- * Returns this candidate's Internet Protocol (IP) address; this can be
- * either an IPv4 address or an IPv6 address.
- *
- * @return this candidate's IPv4 or IPv6 address.
- */
- public String getIP()
- {
- return super.getAttributeAsString(IP_ATTR_NAME);
- }
-
- /**
- * The network index indicating the interface that the candidate belongs to.
- * The network ID is used for diagnostic purposes only in cases where the
- * calling hardware has more than one Network Interface Card.
- *
- * @param network the network index indicating the interface that the
- * candidate belongs to.
- */
- public void setNetwork(int network)
- {
- super.setAttribute(NETWORK_ATTR_NAME, network);
- }
-
- /**
- * Returns the network index indicating the interface that the candidate
- * belongs to. The network ID is used for diagnostic purposes only in cases
- * where the calling hardware has more than one Network Interface Card.
- *
- * @return the network index indicating the interface that the candidate
- * belongs to.
- */
- public int getNetwork()
- {
- return super.getAttributeAsInt(NETWORK_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's port number.
- *
- * @param port this candidate's port number.
- */
- public void setPort(int port)
- {
- super.setAttribute(PORT_ATTR_NAME, port);
- }
-
- /**
- * Returns this candidate's port number.
- *
- * @return this candidate's port number.
- */
- public int getPort()
- {
- return super.getAttributeAsInt(PORT_ATTR_NAME);
- }
-
- /**
- * This candidate's priority as defined in ICE's RFC 5245
- *
- * @param priority this candidate's priority
- */
- public void setPriority(long priority)
- {
- super.setAttribute(PRIORITY_ATTR_NAME, priority);
- }
-
- /**
- * This candidate's priority as defined in ICE's RFC 5245
- *
- * @return this candidate's priority
- */
- public int getPriority()
- {
- return super.getAttributeAsInt(PRIORITY_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's transport protocol.
- *
- * @param protocol this candidate's transport protocol.
- */
- public void setProtocol(String protocol)
- {
- super.setAttribute(PROTOCOL_ATTR_NAME, protocol);
- }
-
- /**
- * Sets this candidate's transport protocol.
- *
- * @return this candidate's transport protocol.
- */
- public String getProtocol()
- {
- return super.getAttributeAsString(PROTOCOL_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's related address as described by ICE's RFC 5245.
- *
- * @param relAddr this candidate's related address as described by ICE's
- * RFC 5245.
- */
- public void setRelAddr(String relAddr)
- {
- super.setAttribute(REL_ADDR_ATTR_NAME, relAddr);
- }
-
- /**
- * Returns this candidate's related address as described by ICE's RFC 5245.
- *
- * @return this candidate's related address as described by ICE's RFC 5245.
- */
- public String getRelAddr()
- {
- return super.getAttributeAsString(REL_ADDR_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's related port as described by ICE's RFC 5245.
- *
- * @param relPort this candidate's related port as described by ICE's
- * RFC 5245.
- */
- public void setRelPort(int relPort)
- {
- super.setAttribute(REL_PORT_ATTR_NAME, relPort);
- }
-
- /**
- * Returns this candidate's related port as described by ICE's RFC 5245.
- *
- * @return this candidate's related port as described by ICE's RFC 5245.
- */
- public int getRelPort()
- {
- return super.getAttributeAsInt(REL_PORT_ATTR_NAME);
- }
-
- /**
- * Sets a Candidate Type as defined in ICE-CORE. The allowable values are
- * "host" for host candidates, "prflx" for peer reflexive candidates,
- * "relay" for relayed candidates, and "srflx" for server reflexive
- * candidates. All allowable values are enumerated in the {@link
- * CandidateType} enum.
- *
- * @param type this candidates' type as per ICE's RFC 5245.
- */
- public void setType(CandidateType type)
- {
- super.setAttribute(TYPE_ATTR_NAME, type);
- }
-
- /**
- * Returns a Candidate Type as defined in ICE-CORE. The allowable values are
- * "host" for host candidates, "prflx" for peer reflexive candidates,
- * "relay" for relayed candidates, and "srflx" for server reflexive
- * candidates. All allowable values are enumerated in the {@link
- * CandidateType} enum.
- *
- * @return this candidates' type as per ICE's RFC 5245.
- */
- public CandidateType getType()
- {
- return CandidateType.valueOf(getAttributeAsString(TYPE_ATTR_NAME));
- }
-
- /**
- * Compares this instance with another CandidatePacketExtension by
- * preference of type: host < local < prflx < srflx < stun < relay.
- *
- * @return 0 if the type are equal. -1 if this instance type is preferred.
- * Otherwise 1.
- */
- public int compareTo(CandidatePacketExtension candidatePacketExtension)
- {
- // If the types are different.
- if(this.getType() != candidatePacketExtension.getType())
- {
- CandidateType[] types = {
- CandidateType.host,
- CandidateType.local,
- CandidateType.prflx,
- CandidateType.srflx,
- CandidateType.stun,
- CandidateType.relay
- };
- for(int i = 0; i < types.length; ++i)
- {
- // this object is preferred.
- if(types[i] == this.getType())
- {
- return -1;
- }
- // the candidatePacketExtension is preferred.
- else if(types[i] == candidatePacketExtension.getType())
- {
- return 1;
- }
- }
- }
- // If the types are equal.
- return 0;
- }
-
- /**
- * Gets the TCP type for this <tt>CandidatePacketExtension</tt>.
- */
- public CandidateTcpType getTcpType()
- {
- String tcpTypeString = getAttributeAsString(TCPTYPE_ATTR_NAME);
- try
- {
- return CandidateTcpType.parse(tcpTypeString);
- }
- catch (IllegalArgumentException iae)
- {
- return null;
- }
- }
-
- /**
- * Sets the TCP type for this <tt>CandidatePacketExtension</tt>.
- * @param tcpType
- */
- public void setTcpType(CandidateTcpType tcpType)
- {
- setAttribute(TCPTYPE_ATTR_NAME, tcpType.toString());
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; +import org.ice4j.ice.*; + +/** + * @author Emil Ivov + */ +public class CandidatePacketExtension extends AbstractPacketExtension + implements Comparable<CandidatePacketExtension> +{ + /** + * The name of the "candidate" element. + */ + public static final String ELEMENT_NAME = "candidate"; + + /** + * The name of the "component" element. + */ + public static final String COMPONENT_ATTR_NAME = "component"; + + /** + * The "component" ID for RTP components. + */ + public static final int RTP_COMPONENT_ID = 1; + + /** + * The "component" ID for RTCP components. + */ + public static final int RTCP_COMPONENT_ID = 2; + + /** + * The name of the "foundation" element. + */ + public static final String FOUNDATION_ATTR_NAME = "foundation"; + + /** + * The name of the "generation" element. + */ + public static final String GENERATION_ATTR_NAME = "generation"; + + /** + * The name of the "id" element. + */ + public static final String ID_ATTR_NAME = "id"; + + /** + * The name of the "ip" element. + */ + public static final String IP_ATTR_NAME = "ip"; + + /** + * The name of the "network" element. + */ + public static final String NETWORK_ATTR_NAME = "network"; + + /** + * The name of the "port" element. + */ + public static final String PORT_ATTR_NAME = "port"; + + /** + * The name of the "priority" element. + */ + public static final String PRIORITY_ATTR_NAME = "priority"; + + /** + * The name of the "protocol" element. + */ + public static final String PROTOCOL_ATTR_NAME = "protocol"; + + /** + * The name of the "rel-addr" element. + */ + public static final String REL_ADDR_ATTR_NAME = "rel-addr"; + + /** + * The name of the "rel-port" element. + */ + public static final String REL_PORT_ATTR_NAME = "rel-port"; + + /** + * The name of the "type" element. + */ + public static final String TYPE_ATTR_NAME = "type"; + + /** + * The name of the "tcptype" element. + */ + public static final String TCPTYPE_ATTR_NAME = "tcptype"; + + /** + * Creates a new {@link CandidatePacketExtension} + */ + public CandidatePacketExtension() + { + super(null, ELEMENT_NAME); + } + + /** + * Creates a new {@link CandidatePacketExtension} with the specified + * <tt>elementName</tt> so that this class would be usable as a + * <tt>RemoteCandidatePacketExtension</tt> parent. + * + * @param elementName the element name that this instance should be using. + */ + protected CandidatePacketExtension(String elementName) + { + super(null, elementName); + } + + /** + * Sets a component ID as defined in ICE-CORE. + * + * @param component a component ID as defined in ICE-CORE. + */ + public void setComponent(int component) + { + super.setAttribute(COMPONENT_ATTR_NAME, component); + } + + /** + * Returns a component ID as defined in ICE-CORE. + * + * @return a component ID as defined in ICE-CORE. + */ + public int getComponent() + { + return super.getAttributeAsInt(COMPONENT_ATTR_NAME); + } + + /** + * Sets the candidate foundation as defined in ICE-CORE. + * + * @param foundation the candidate foundation as defined in ICE-CORE. + */ + public void setFoundation(String foundation) + { + super.setAttribute(FOUNDATION_ATTR_NAME, foundation); + } + + /** + * Returns the candidate foundation as defined in ICE-CORE. + * + * @return the candidate foundation as defined in ICE-CORE. + */ + public String getFoundation() + { + return super.getAttributeAsString(FOUNDATION_ATTR_NAME); + } + + /** + * Sets this candidate's generation index. A generation is an index, + * starting at 0, that enables the parties to keep track of updates to the + * candidate throughout the life of the session. For details, see the ICE + * Restarts section of XEP-0176. + * + * @param generation this candidate's generation index. + */ + public void setGeneration(int generation) + { + super.setAttribute(GENERATION_ATTR_NAME, generation); + } + + /** + * Returns this candidate's generation. A generation is an index, starting at + * 0, that enables the parties to keep track of updates to the candidate + * throughout the life of the session. For details, see the ICE Restarts + * section of XEP-0176. + * + * @return this candidate's generation index. + */ + public int getGeneration() + { + return super.getAttributeAsInt(GENERATION_ATTR_NAME); + } + + /** + * Sets this candidates's unique identifier <tt>String</tt>. + * + * @param id this candidates's unique identifier <tt>String</tt> + */ + public void setID(String id) + { + super.setAttribute(ID_ATTR_NAME, id); + } + + /** + * Returns this candidates's unique identifier <tt>String</tt>. + * + * @return this candidates's unique identifier <tt>String</tt> + */ + public String getID() + { + return super.getAttributeAsString(ID_ATTR_NAME); + } + + /** + * Sets this candidate's Internet Protocol (IP) address; this can be either + * an IPv4 address or an IPv6 address. + * + * @param ip this candidate's IPv4 or IPv6 address. + */ + public void setIP(String ip) + { + super.setAttribute(IP_ATTR_NAME, ip); + } + + /** + * Returns this candidate's Internet Protocol (IP) address; this can be + * either an IPv4 address or an IPv6 address. + * + * @return this candidate's IPv4 or IPv6 address. + */ + public String getIP() + { + return super.getAttributeAsString(IP_ATTR_NAME); + } + + /** + * The network index indicating the interface that the candidate belongs to. + * The network ID is used for diagnostic purposes only in cases where the + * calling hardware has more than one Network Interface Card. + * + * @param network the network index indicating the interface that the + * candidate belongs to. + */ + public void setNetwork(int network) + { + super.setAttribute(NETWORK_ATTR_NAME, network); + } + + /** + * Returns the network index indicating the interface that the candidate + * belongs to. The network ID is used for diagnostic purposes only in cases + * where the calling hardware has more than one Network Interface Card. + * + * @return the network index indicating the interface that the candidate + * belongs to. + */ + public int getNetwork() + { + return super.getAttributeAsInt(NETWORK_ATTR_NAME); + } + + /** + * Sets this candidate's port number. + * + * @param port this candidate's port number. + */ + public void setPort(int port) + { + super.setAttribute(PORT_ATTR_NAME, port); + } + + /** + * Returns this candidate's port number. + * + * @return this candidate's port number. + */ + public int getPort() + { + return super.getAttributeAsInt(PORT_ATTR_NAME); + } + + /** + * This candidate's priority as defined in ICE's RFC 5245 + * + * @param priority this candidate's priority + */ + public void setPriority(long priority) + { + super.setAttribute(PRIORITY_ATTR_NAME, priority); + } + + /** + * This candidate's priority as defined in ICE's RFC 5245 + * + * @return this candidate's priority + */ + public int getPriority() + { + return super.getAttributeAsInt(PRIORITY_ATTR_NAME); + } + + /** + * Sets this candidate's transport protocol. + * + * @param protocol this candidate's transport protocol. + */ + public void setProtocol(String protocol) + { + super.setAttribute(PROTOCOL_ATTR_NAME, protocol); + } + + /** + * Sets this candidate's transport protocol. + * + * @return this candidate's transport protocol. + */ + public String getProtocol() + { + return super.getAttributeAsString(PROTOCOL_ATTR_NAME); + } + + /** + * Sets this candidate's related address as described by ICE's RFC 5245. + * + * @param relAddr this candidate's related address as described by ICE's + * RFC 5245. + */ + public void setRelAddr(String relAddr) + { + super.setAttribute(REL_ADDR_ATTR_NAME, relAddr); + } + + /** + * Returns this candidate's related address as described by ICE's RFC 5245. + * + * @return this candidate's related address as described by ICE's RFC 5245. + */ + public String getRelAddr() + { + return super.getAttributeAsString(REL_ADDR_ATTR_NAME); + } + + /** + * Sets this candidate's related port as described by ICE's RFC 5245. + * + * @param relPort this candidate's related port as described by ICE's + * RFC 5245. + */ + public void setRelPort(int relPort) + { + super.setAttribute(REL_PORT_ATTR_NAME, relPort); + } + + /** + * Returns this candidate's related port as described by ICE's RFC 5245. + * + * @return this candidate's related port as described by ICE's RFC 5245. + */ + public int getRelPort() + { + return super.getAttributeAsInt(REL_PORT_ATTR_NAME); + } + + /** + * Sets a Candidate Type as defined in ICE-CORE. The allowable values are + * "host" for host candidates, "prflx" for peer reflexive candidates, + * "relay" for relayed candidates, and "srflx" for server reflexive + * candidates. All allowable values are enumerated in the {@link + * CandidateType} enum. + * + * @param type this candidates' type as per ICE's RFC 5245. + */ + public void setType(CandidateType type) + { + super.setAttribute(TYPE_ATTR_NAME, type); + } + + /** + * Returns a Candidate Type as defined in ICE-CORE. The allowable values are + * "host" for host candidates, "prflx" for peer reflexive candidates, + * "relay" for relayed candidates, and "srflx" for server reflexive + * candidates. All allowable values are enumerated in the {@link + * CandidateType} enum. + * + * @return this candidates' type as per ICE's RFC 5245. + */ + public CandidateType getType() + { + return CandidateType.valueOf(getAttributeAsString(TYPE_ATTR_NAME)); + } + + /** + * Compares this instance with another CandidatePacketExtension by + * preference of type: host < local < prflx < srflx < stun < relay. + * + * @return 0 if the type are equal. -1 if this instance type is preferred. + * Otherwise 1. + */ + public int compareTo(CandidatePacketExtension candidatePacketExtension) + { + // If the types are different. + if(this.getType() != candidatePacketExtension.getType()) + { + CandidateType[] types = { + CandidateType.host, + CandidateType.local, + CandidateType.prflx, + CandidateType.srflx, + CandidateType.stun, + CandidateType.relay + }; + for(int i = 0; i < types.length; ++i) + { + // this object is preferred. + if(types[i] == this.getType()) + { + return -1; + } + // the candidatePacketExtension is preferred. + else if(types[i] == candidatePacketExtension.getType()) + { + return 1; + } + } + } + // If the types are equal. + return 0; + } + + /** + * Gets the TCP type for this <tt>CandidatePacketExtension</tt>. + */ + public CandidateTcpType getTcpType() + { + String tcpTypeString = getAttributeAsString(TCPTYPE_ATTR_NAME); + try + { + return CandidateTcpType.parse(tcpTypeString); + } + catch (IllegalArgumentException iae) + { + return null; + } + } + + /** + * Sets the TCP type for this <tt>CandidatePacketExtension</tt>. + * @param tcpType + */ + public void setTcpType(CandidateTcpType tcpType) + { + setAttribute(TCPTYPE_ATTR_NAME, tcpType.toString()); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CryptoPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CryptoPacketExtension.java index 69b2e47..3ccbd72 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CryptoPacketExtension.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CryptoPacketExtension.java @@ -17,6 +17,8 @@ */ package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle; +import java.util.Objects; + import ch.imvs.sdes4j.srtp.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.*; @@ -332,4 +334,14 @@ public class CryptoPacketExtension } return false; } + + @Override + public int hashCode() + { + return Objects.hash( + getCryptoSuite(), + getKeyParams(), + getSessionParams(), + getTag()); + } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java index 1112c8c..173701f 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java @@ -191,6 +191,27 @@ public class IceUdpTransportPacketExtension } /** + * Removes given <tt>PacketExtension</tt> from the list of child packet + * extensions. <tt>CandidatePacketExtension</tt> are not taken into account + * in this method and {@link #removeCandidate(CandidatePacketExtension)} + * should be used instead. + * + * @param childExtension <tt>PacketExtension</tt> instance to be removed + * from child packet extensions list. + * + * @return <tt>true</tt> if given <tt>childExtension</tt> has been in the + * list and was removed or <tt>false</tt> otherwise. + */ + public boolean removeChildExtension(PacketExtension childExtension) + { + List<? extends PacketExtension> childExtensions + = super.getChildExtensions(); + + return childExtensions != null + && childExtensions.remove(childExtension); + } + + /** * Returns the list of {@link CandidatePacketExtension}s currently * registered with this transport. * diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQ.java index 9183fa0..65b0849 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQ.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQ.java @@ -21,6 +21,7 @@ import java.math.*; import java.security.*; import java.util.*; +import net.java.sip.communicator.service.protocol.jabber.*; import org.jivesoftware.smack.packet.*; /** @@ -62,7 +63,8 @@ public class JingleIQ extends IQ * The name of the argument that contains the session id. */ public static final String SID_ATTR_NAME = "sid"; - + + /** * The <tt>JingleAction</tt> that describes the purpose of this * <tt>jingle</tt> element. @@ -104,8 +106,8 @@ public class JingleIQ extends IQ * The list of "content" elements included in this IQ. */ private final List<ContentPacketExtension> contentList - = new ArrayList<ContentPacketExtension>(); - + = new ArrayList<ContentPacketExtension>(); + /** * Returns the XML string of this Jingle IQ's "section" sub-element. * @@ -124,17 +126,18 @@ public class JingleIQ extends IQ if( initiator != null) bldr.append(" " + INITIATOR_ATTR_NAME - + "='" + getInitiator() + "'"); + + "='" + getInitiator() + "'"); if( responder != null) bldr.append(" " + RESPONDER_ATTR_NAME - + "='" + getResponder() + "'"); + + "='" + getResponder() + "'"); bldr.append(" " + SID_ATTR_NAME - + "='" + getSID() + "'"); - - String extensionsXML = getExtensionsXML(); + + "='" + getSID() + "'"); + CharSequence extensionsXMLSeq = getExtensionsXML(); + String extensionsXML = extensionsXMLSeq.toString(); + if ((contentList.size() == 0) && (reason == null) && (sessionInfo == null) @@ -348,7 +351,7 @@ public class JingleIQ extends IQ * otherwise. */ public boolean containsContentChildOfType( - Class<? extends PacketExtension> contentType) + Class<? extends PacketExtension> contentType) { if(getContentForType(contentType) != null) return true; @@ -369,14 +372,14 @@ public class JingleIQ extends IQ * found. */ public ContentPacketExtension getContentForType( - Class<? extends PacketExtension> contentType) + Class<? extends PacketExtension> contentType) { synchronized(contentList) { for(ContentPacketExtension content : contentList) { PacketExtension child - = content.getFirstChildOfType(contentType); + = content.getFirstChildOfType(contentType); if(child != null) return content; } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java index 11f909a..49934e5 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java @@ -20,6 +20,7 @@ package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle; import net.java.sip.communicator.impl.protocol.jabber.extensions.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jitsimeet.*; +import net.java.sip.communicator.service.protocol.jabber.*; import org.jivesoftware.smack.provider.*; import org.xmlpull.v1.*; @@ -37,174 +38,187 @@ public class JingleIQProvider implements IQProvider */ public JingleIQProvider() { - ProviderManager providerManager = ProviderManager.getInstance(); + + AbstractSmackInteroperabilityLayer smackInteroperabilityLayer = + AbstractSmackInteroperabilityLayer.getInstance(); //<description/> provider - providerManager.addExtensionProvider( - RtpDescriptionPacketExtension.ELEMENT_NAME, - RtpDescriptionPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider - <RtpDescriptionPacketExtension>( - RtpDescriptionPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + RtpDescriptionPacketExtension.ELEMENT_NAME, + RtpDescriptionPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <RtpDescriptionPacketExtension>( + RtpDescriptionPacketExtension.class)); //<payload-type/> provider - providerManager.addExtensionProvider( - PayloadTypePacketExtension.ELEMENT_NAME, - RtpDescriptionPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider - <PayloadTypePacketExtension>( - PayloadTypePacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + PayloadTypePacketExtension.ELEMENT_NAME, + RtpDescriptionPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <PayloadTypePacketExtension>( + PayloadTypePacketExtension.class)); //<parameter/> provider - providerManager.addExtensionProvider( - ParameterPacketExtension.ELEMENT_NAME, - RtpDescriptionPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider - <ParameterPacketExtension>(ParameterPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + ParameterPacketExtension.ELEMENT_NAME, + RtpDescriptionPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <ParameterPacketExtension> + (ParameterPacketExtension.class)); //<rtp-hdrext/> provider - providerManager.addExtensionProvider( - RTPHdrExtPacketExtension.ELEMENT_NAME, - RTPHdrExtPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider - <RTPHdrExtPacketExtension>(RTPHdrExtPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + RTPHdrExtPacketExtension.ELEMENT_NAME, + RTPHdrExtPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <RTPHdrExtPacketExtension> + (RTPHdrExtPacketExtension.class)); // <sctpmap/> provider - providerManager.addExtensionProvider( - SctpMapExtension.ELEMENT_NAME, - SctpMapExtension.NAMESPACE, - new SctpMapExtensionProvider()); + smackInteroperabilityLayer.addExtensionProvider( + SctpMapExtension.ELEMENT_NAME, + SctpMapExtension.NAMESPACE, + new SctpMapExtensionProvider()); //<encryption/> provider - providerManager.addExtensionProvider( - EncryptionPacketExtension.ELEMENT_NAME, - RtpDescriptionPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider - <EncryptionPacketExtension>(EncryptionPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + EncryptionPacketExtension.ELEMENT_NAME, + RtpDescriptionPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <EncryptionPacketExtension> + (EncryptionPacketExtension.class)); //<zrtp-hash/> provider - providerManager.addExtensionProvider( - ZrtpHashPacketExtension.ELEMENT_NAME, - ZrtpHashPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider - <ZrtpHashPacketExtension>(ZrtpHashPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + ZrtpHashPacketExtension.ELEMENT_NAME, + ZrtpHashPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <ZrtpHashPacketExtension> + (ZrtpHashPacketExtension.class)); //<crypto/> provider - providerManager.addExtensionProvider( - CryptoPacketExtension.ELEMENT_NAME, - RtpDescriptionPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider - <CryptoPacketExtension>(CryptoPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + CryptoPacketExtension.ELEMENT_NAME, + RtpDescriptionPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <CryptoPacketExtension> + (CryptoPacketExtension.class)); // <bundle/> provider - providerManager.addExtensionProvider( - BundlePacketExtension.ELEMENT_NAME, - BundlePacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider - <BundlePacketExtension>(BundlePacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + BundlePacketExtension.ELEMENT_NAME, + BundlePacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <BundlePacketExtension> + (BundlePacketExtension.class)); // <group/> provider - providerManager.addExtensionProvider( - GroupPacketExtension.ELEMENT_NAME, - GroupPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider - <GroupPacketExtension>(GroupPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + GroupPacketExtension.ELEMENT_NAME, + GroupPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <GroupPacketExtension>(GroupPacketExtension.class)); //ice-udp transport - providerManager.addExtensionProvider( - IceUdpTransportPacketExtension.ELEMENT_NAME, - IceUdpTransportPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider<IceUdpTransportPacketExtension>( - IceUdpTransportPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + IceUdpTransportPacketExtension.ELEMENT_NAME, + IceUdpTransportPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <IceUdpTransportPacketExtension>( + IceUdpTransportPacketExtension.class)); //<raw-udp/> provider - providerManager.addExtensionProvider( - RawUdpTransportPacketExtension.ELEMENT_NAME, - RawUdpTransportPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider<RawUdpTransportPacketExtension>( - RawUdpTransportPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + RawUdpTransportPacketExtension.ELEMENT_NAME, + RawUdpTransportPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <RawUdpTransportPacketExtension>( + RawUdpTransportPacketExtension.class)); //ice-udp <candidate/> provider - providerManager.addExtensionProvider( - CandidatePacketExtension.ELEMENT_NAME, - IceUdpTransportPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider<CandidatePacketExtension>( - CandidatePacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + CandidatePacketExtension.ELEMENT_NAME, + IceUdpTransportPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <CandidatePacketExtension>( + CandidatePacketExtension.class)); //raw-udp <candidate/> provider - providerManager.addExtensionProvider( - CandidatePacketExtension.ELEMENT_NAME, - RawUdpTransportPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider<CandidatePacketExtension>( - CandidatePacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + CandidatePacketExtension.ELEMENT_NAME, + RawUdpTransportPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <CandidatePacketExtension>( + CandidatePacketExtension.class)); //ice-udp <remote-candidate/> provider - providerManager.addExtensionProvider( - RemoteCandidatePacketExtension.ELEMENT_NAME, - IceUdpTransportPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider<RemoteCandidatePacketExtension>( - RemoteCandidatePacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + RemoteCandidatePacketExtension.ELEMENT_NAME, + IceUdpTransportPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <RemoteCandidatePacketExtension>( + RemoteCandidatePacketExtension.class)); //inputevt <inputevt/> provider - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( InputEvtPacketExtension.ELEMENT_NAME, InputEvtPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<InputEvtPacketExtension>( InputEvtPacketExtension.class)); //coin <conference-info/> provider - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( CoinPacketExtension.ELEMENT_NAME, CoinPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<CoinPacketExtension>( CoinPacketExtension.class)); // DTLS-SRTP - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( DtlsFingerprintPacketExtension.ELEMENT_NAME, DtlsFingerprintPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider - <DtlsFingerprintPacketExtension>( + <DtlsFingerprintPacketExtension>( DtlsFingerprintPacketExtension.class)); /* * XEP-0251: Jingle Session Transfer <transfer/> and <transferred> * providers */ - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( TransferPacketExtension.ELEMENT_NAME, TransferPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<TransferPacketExtension>( TransferPacketExtension.class)); - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( TransferredPacketExtension.ELEMENT_NAME, TransferredPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<TransferredPacketExtension>( TransferredPacketExtension.class)); //conference description <callid/> provider - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( ConferenceDescriptionPacketExtension.CALLID_ELEM_NAME, ConferenceDescriptionPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<CallIdPacketExtension>( CallIdPacketExtension.class)); //rtcp-fb - providerManager.addExtensionProvider( - RtcpFbPacketExtension.ELEMENT_NAME, - RtcpFbPacketExtension.NAMESPACE, - new DefaultPacketExtensionProvider<RtcpFbPacketExtension>( - RtcpFbPacketExtension.class)); + smackInteroperabilityLayer.addExtensionProvider( + RtcpFbPacketExtension.ELEMENT_NAME, + RtcpFbPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider<RtcpFbPacketExtension>( + RtcpFbPacketExtension.class)); //rtcp-mux - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( RtcpmuxPacketExtension.ELEMENT_NAME, IceUdpTransportPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<RtcpmuxPacketExtension>( RtcpmuxPacketExtension.class)); //ssrcInfo - providerManager.addExtensionProvider( + smackInteroperabilityLayer.addExtensionProvider( SSRCInfoPacketExtension.ELEMENT_NAME, SSRCInfoPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider<SSRCInfoPacketExtension>( diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/ComponentVersionsExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/ComponentVersionsExtension.java new file mode 100644 index 0000000..bcc3397 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/ComponentVersionsExtension.java @@ -0,0 +1,135 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jitsimeet; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +/** + * The packet extension is used by Jicofo to broadcast versions of all video + * conferencing system components. This packets extension is added to jicofo's + * MUC presence. It will contain {@link Component} children which carry each + * component's name and version. + * + * @author Pawel Domas + */ +public class ComponentVersionsExtension + extends AbstractPacketExtension +{ + /** + * The XML element name of {@link ComponentVersionsExtension}. + */ + public static final String ELEMENT_NAME = "versions"; + + /** + * The name of XML sub-elements which carry the info about particular + * component's version. + */ + public static final String COMPONENT_ELEMENT_NAME = "component"; + + /** + * Constant for {@link Component} name used to signal the version of + * conference focus. + */ + public static final String COMPONENT_FOCUS = "focus"; + + /** + * Constant for {@link Component} name used to signal the version of + * XMPP server. + */ + public static final String COMPONENT_XMPP_SERVER = "xmpp"; + + /** + * Constant for {@link Component} name used to signal the version of + * the videobridge. + */ + public static final String COMPONENT_VIDEOBRIDGE = "videobridge"; + + /** + * The XML element namespace of {@link ComponentVersionsExtension}. + */ + public static final String NAMESPACE = "http://jitsi.org/jitmeet"; + + /** + * Creates an {@link AbstractPacketExtension} instance for the specified + * <tt>namespace</tt> and <tt>elementName</tt>. + */ + public ComponentVersionsExtension() + { + super(NAMESPACE, ELEMENT_NAME); + } + + /** + * Adds component's version to this extension. + * + * @param componentName the name of the component for which + * child {@link Component} extension will be added. + * @param versionStr human readable string that describes component's + * version. + */ + public void addComponentVersion(String componentName, String versionStr) + { + Component v = new Component(); + + v.setName(componentName); + v.setText(versionStr); + + addChildExtension(v); + } + + /** + * Component child element of {@link ComponentVersionsExtension}. The name + * of the component is carried in name attribute and the version string is + * the text value. + */ + public class Component + extends AbstractPacketExtension + { + /** + * The name of that attribute that carries component's name. + */ + private final String NAME_ATTR_NAME = "name"; + + /** + * Creates new instance of {@link Component} packet extension. + */ + public Component() + { + super(NAMESPACE, COMPONENT_ELEMENT_NAME); + } + + /** + * Returns the value of the name attribute. + * @return <tt>String</tt> which describes the name of video + * conferencing system component. + */ + public String getName() + { + return getAttributeAsString(NAME_ATTR_NAME); + } + + /** + * Sets new value for the component's name attribute. + * @param name a <tt>String</tt> which describes the name of video + * conferencing system component. + */ + public void setName(String name) + { + setAttribute(NAME_ATTR_NAME, name); + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/SSRCInfoPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/SSRCInfoPacketExtension.java index 239c708..7384c9e 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/SSRCInfoPacketExtension.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/SSRCInfoPacketExtension.java @@ -1,8 +1,19 @@ /* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * - * Distributable under LGPL license. - * See terms of license at gnu.org. + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package net.java.sip.communicator.impl.protocol.jabber.extensions.jitsimeet; diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/VideoMutedExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/VideoMutedExtension.java new file mode 100644 index 0000000..cf76d89 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/VideoMutedExtension.java @@ -0,0 +1,70 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jitsimeet; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +/** + * Video muted extension that is included in users presence in Jitsi-meet + * conferences. It does carry the info about user's video muted status. + * + * @author Pawel Domas + */ +public class VideoMutedExtension + extends AbstractPacketExtension +{ + /** + * The namespace of this packet extension. + */ + public static final String NAMESPACE = "http://jitsi.org/jitmeet/video"; + + /** + * XML element name of this packet extension. + */ + public static final String ELEMENT_NAME = "videomuted"; + + /** + * Creates new instance of <tt>VideoMutedExtension</tt>. + */ + public VideoMutedExtension() + { + super(NAMESPACE, ELEMENT_NAME); + } + + /** + * Check whether or not user's video is in muted status. + * @return <tt>true</tt> if muted, <tt>false</tt> if unmuted or + * <tt>null</tt> if no valid info found in the extension body. + */ + public Boolean isVideoMuted() + { + return Boolean.valueOf(getText()); + } + + /** + * Sets user's video muted status. + * + * @param videoMuted <tt>true</tt> or <tt>false</tt> which indicates video + * muted status of the user. + */ + public void setVideoMuted(Boolean videoMuted) + { + setText( + String.valueOf(videoMuted)); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java b/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java index 93cdead..8094f68 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java @@ -179,12 +179,17 @@ public class JingleUtils else paramsMap.put(paramName, param.getValue()); } - - // video-related attributes in payload-type element + for(String attr : payloadType.getAttributeNames()) { + //video-related attributes in payload-type element if(attr.equals("width") || attr.equals("height")) paramsMap.put(attr, payloadType.getAttributeAsString(attr)); + + //update ptime with the actual value from the payload + if (attr.equals(PayloadTypePacketExtension.PTIME_ATTR_NAME)) + advancedMap.put(PayloadTypePacketExtension.PTIME_ATTR_NAME, + Integer.toString(payloadType.getPtime())); } //now create the format. diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockContactGroup.java b/src/net/java/sip/communicator/impl/protocol/mock/MockContactGroup.java index fa4fff1..e6cdd36 100644 --- a/src/net/java/sip/communicator/impl/protocol/mock/MockContactGroup.java +++ b/src/net/java/sip/communicator/impl/protocol/mock/MockContactGroup.java @@ -506,6 +506,31 @@ public class MockContactGroup return true; } + @Override + public int hashCode() + { + List<Object> objects = new ArrayList<Object>(); + objects.add(getGroupName()); + objects.add(getUID()); + objects.add(countContacts()); + objects.add(countSubgroups()); + + //traverse child contacts + for (Contact c : contacts) + { + objects.add(c.getAddress()); + } + + + //traverse subgroups + for (ContactGroup g : subGroups) + { + objects.add(g.getGroupName()); + } + + return Objects.hash(objects.toArray()); + } + public void setPersistent(boolean isPersistent) { this.isPersistent = isPersistent; diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockProvider.java b/src/net/java/sip/communicator/impl/protocol/mock/MockProvider.java index bcff799..a7b6e5e 100644 --- a/src/net/java/sip/communicator/impl/protocol/mock/MockProvider.java +++ b/src/net/java/sip/communicator/impl/protocol/mock/MockProvider.java @@ -294,6 +294,15 @@ public class MockProvider } /** + * Always true. + */ + @Override + public boolean validateContactAddress(String contactId, List<String> result) + { + return true; + } + + /** * Mock implementation of the corresponding ProtocolProviderService method. * We have no icon corresponding to this protocol provider. */ diff --git a/src/net/java/sip/communicator/impl/protocol/sip/AddressResolverImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/AddressResolverImpl.java index eba0da6..4fd1b6f 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/AddressResolverImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/AddressResolverImpl.java @@ -70,18 +70,8 @@ public class AddressResolverImpl // if it is a textual IP address, do no try to resolve it if(NetworkUtils.isValidIPAddress(hostAddress)) { - byte[] addr = null; - - addr = NetworkUtils.strToIPv4(hostAddress); - - // not an IPv4, try IPv6 - if (addr == null) - { - addr = NetworkUtils.strToIPv6(hostAddress); - } - InetSocketAddress hostSocketAddress = new InetSocketAddress( - InetAddress.getByAddress(hostAddress, addr), + NetworkUtils.getInetAddress(hostAddress), inputAddress.getPort()); return new HopImpl(hostSocketAddress.getHostName(), inputAddress.getPort(), diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java index c4193a5..b626dbb 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java @@ -87,7 +87,7 @@ public class CallPeerMediaHandlerSipImpl * Whether other party is able to change video quality settings. * Normally its whether we have detected existence of imageattr in sdp. */ - boolean supportQualityControls; + private boolean supportQualityControls; /** * The current quality controls for this peer media handler if any. @@ -98,7 +98,7 @@ public class CallPeerMediaHandlerSipImpl * The lock we use to make sure that we won't be processing a second * offer/answer exchange while a . */ - private Object offerAnswerLock = new Object(); + private final Object offerAnswerLock = new Object(); /** * Creates a new handler that will be managing media streams for @@ -164,7 +164,7 @@ public class CallPeerMediaHandlerSipImpl throws OperationFailedException { //Audio Media Description - Vector<MediaDescription> mediaDescs = createMediaDescriptions(); + List<MediaDescription> mediaDescs = createMediaDescriptions(); //wrap everything up in a session description String userName @@ -197,7 +197,7 @@ public class CallPeerMediaHandlerSipImpl * for reasons like - problems with device interaction, allocating ports, * etc. */ - private Vector<MediaDescription> createMediaDescriptions() + private List<MediaDescription> createMediaDescriptions() throws OperationFailedException { //Audio Media Description @@ -215,12 +215,16 @@ public class CallPeerMediaHandlerSipImpl receiveQualityPreset = qualityControls.getRemoteSendMaxPreset(); } - for (MediaType mediaType : MediaType.values()) + for (MediaType mediaType : new MediaType[] { MediaType.AUDIO , MediaType.VIDEO}) { MediaDevice dev = getDefaultDevice(mediaType); if (!isDeviceActive(dev, sendQualityPreset, receiveQualityPreset)) + { + logger.warn("No active device for " + mediaType.toString() + + " was found!"); continue; + } MediaDirection direction = dev.getDirection().and(getDirectionUserPreference(mediaType)); @@ -339,7 +343,7 @@ public class CallPeerMediaHandlerSipImpl * it is known that the other endpoint supports this * profile" and "[o]ther profiles MAY also be used." */ - updateMediaDescriptionForZrtp(mediaType, md, null); + updateMediaDescriptionForZrtp(mediaType, md); if (SrtpControl.RTP_SAVP.equals(proto) || SrtpControl.RTP_SAVPF.equals(proto)) { @@ -400,7 +404,7 @@ public class CallPeerMediaHandlerSipImpl { //create the media descriptions reflecting our current state. - Vector<MediaDescription> newMediaDescs = createMediaDescriptions(); + List<MediaDescription> newMediaDescs = createMediaDescriptions(); SessionDescription newOffer = SdpUtils.createSessionUpdateDescription( sdescToUpdate, getTransportManager().getLastUsedLocalHost(), @@ -510,7 +514,7 @@ public class CallPeerMediaHandlerSipImpl throws OperationFailedException, IllegalArgumentException { - Vector<MediaDescription> answerDescriptions + List<MediaDescription> answerDescriptions = createMediaDescriptionsForAnswer(newOffer); // wrap everything up in a session description SessionDescription newAnswer @@ -555,10 +559,11 @@ public class CallPeerMediaHandlerSipImpl boolean rejectedAvpOfferDueToSavpMandatory = false; AccountID accountID = getPeer().getProtocolProvider().getAccountID(); - int savpOption - = accountID.getAccountPropertyBoolean( - ProtocolProviderFactory.DEFAULT_ENCRYPTION, - true) + boolean useDefaultEncryption = + accountID.getAccountPropertyBoolean( + ProtocolProviderFactory.DEFAULT_ENCRYPTION , true); + + int savpOption = useDefaultEncryption ? accountID.getAccountPropertyInt( ProtocolProviderFactory.SAVP_OPTION, ProtocolProviderFactory.SAVP_OFF) @@ -638,7 +643,7 @@ public class CallPeerMediaHandlerSipImpl { mutuallySupportedFormats = null; } - else if(mediaType.equals(MediaType.VIDEO) + else if(MediaType.VIDEO.equals(mediaType) && (qualityControls != null)) { /* @@ -705,18 +710,18 @@ public class CallPeerMediaHandlerSipImpl = getTransportManager().getStreamConnector(mediaType); // check for options from remote party and set them locally - if(mediaType.equals(MediaType.VIDEO)) + if(MediaType.VIDEO.equals(mediaType)) { // update stream MediaStream stream = getStream(MediaType.VIDEO); - if(stream != null && dev != null) + if(stream != null) { List<MediaFormat> fmts = intersectFormats( getLocallySupportedFormats(dev), remoteFormats); - if(fmts.size() > 0) + if(!fmts.isEmpty()) { MediaFormat fmt = fmts.get(0); @@ -809,7 +814,7 @@ public class CallPeerMediaHandlerSipImpl { if(remoteDescriptions.size() > 1) { - if(mediaType.equals(MediaType.AUDIO)) + if(MediaType.AUDIO.equals(mediaType)) { masterStream = true; masterStreamSet = true; @@ -1192,8 +1197,7 @@ public class CallPeerMediaHandlerSipImpl */ private boolean updateMediaDescriptionForZrtp( MediaType mediaType, - MediaDescription localMd, - MediaDescription remoteMd) + MediaDescription localMd) { MediaAwareCallPeer<?, ?, ?> peer = getPeer(); AccountID accountID = peer.getProtocolProvider().getAccountID(); @@ -1499,7 +1503,7 @@ public class CallPeerMediaHandlerSipImpl // check for options from remote party and set // is quality controls supported - if(mediaType.equals(MediaType.VIDEO)) + if(MediaType.VIDEO.equals(mediaType)) { supportQualityControls = SdpUtils.containsAttribute(mediaDescription, "imageattr"); @@ -1544,7 +1548,7 @@ public class CallPeerMediaHandlerSipImpl { if(remoteDescriptions.size() > 1) { - if(mediaType.equals(MediaType.AUDIO)) + if(MediaType.AUDIO.equals(mediaType)) { masterStream = true; masterStreamSet = true; @@ -1882,7 +1886,7 @@ public class CallPeerMediaHandlerSipImpl // ZRTP else if(srtpControlType == SrtpControlType.ZRTP) { - if(updateMediaDescriptionForZrtp(mediaType, localMd, remoteMd)) + if(updateMediaDescriptionForZrtp(mediaType, localMd)) { // Stop once an encryption advertisement has been chosen. return; diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java index fa0e08e..62711c7 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java @@ -61,15 +61,27 @@ public class CallSipImpl * Name of extra INVITE header which specifies name of MUC room that is * hosting the Jitsi Meet conference. */ - public static final String JITSI_MEET_ROOM_HEADER - = "Jitsi-Conference-Room"; + public String JITSI_MEET_ROOM_HEADER = "Jitsi-Conference-Room"; + + /** + * Property name of extra INVITE header which specifies name of MUC room + * that is hosting the Jitsi Meet conference. + */ + private static final String JITSI_MEET_ROOM_HEADER_PROPERTY + = "JITSI_MEET_ROOM_HEADER_NAME"; + + /** + * Property name of extra INVITE header which specifies password required + * to enter MUC room that is hosting the Jitsi Meet conference. + */ + public String JITSI_MEET_ROOM_PASS_HEADER = "Jitsi-Conference-Room-Pass"; /** * Name of extra INVITE header which specifies password required to enter * MUC room that is hosting the Jitsi Meet conference. */ - public static final String JITSI_MEET_ROOM_PASS_HEADER - = "Jitsi-Conference-Room-Pass"; + private static final String JITSI_MEET_ROOM_PASS_HEADER_PROPERTY + = "JITSI_MEET_ROOM_PASS_HEADER_NAME"; /** * Custom header included in initial desktop sharing call creation. @@ -78,16 +90,27 @@ public class CallSipImpl public static final String DS_SHARING_HEADER = "X-Desktop-Share"; /** - * When starting call we may have quality preferences we must use - * for the call. + * Custom header name prefix that can be added to the call instance. + * Several headers can be specified in the form of: + * EXTRA_HEADER_NAME.1=... + * EXTRA_HEADER_NAME.2=... + * Index starting from 1. */ - private QualityPreset initialQualityPreferences; + public static final String EXTRA_HEADER_NAME = "EXTRA_HEADER_NAME"; /** - * A reference to the <tt>SipMessageFactory</tt> instance that we should - * use when creating requests. + * Custom header value prefix that can be added to the call instance. + * Several headers can be specified in the form of: + * EXTRA_HEADER_VALUE.1=... + * EXTRA_HEADER_VALUE.2=... + * Index starting from 1. */ - private final SipMessageFactory messageFactory; + public static final String EXTRA_HEADER_VALUE = "EXTRA_HEADER_VALUE"; + + /** + * Maximum number of retransmissions that will be sent. + */ + private static final int MAX_RETRANSMISSIONS = 3; /** * The name of the property under which the user may specify the number of @@ -95,19 +118,27 @@ public class CallSipImpl * 180. */ private static final String RETRANSMITS_RINGING_INTERVAL - = "net.java.sip.communicator.impl.protocol.sip" - + ".RETRANSMITS_RINGING_INTERVAL"; + = "net.java.sip.communicator.impl.protocol.sip" + + ".RETRANSMITS_RINGING_INTERVAL"; /** - * The default amount of time (in milliseconds) for the initial interval for - * retransmissions of response 180. - */ + * The default amount of time (in milliseconds) for the initial interval for + * retransmissions of response 180. + */ private static final int DEFAULT_RETRANSMITS_RINGING_INTERVAL = 500; /** - * Maximum number of retransmissions that will be sent. + * When starting call we may have quality preferences we must use + * for the call. */ - private static final int MAX_RETRANSMISSIONS = 3; + private QualityPreset initialQualityPreferences; + + /** + * A reference to the <tt>SipMessageFactory</tt> instance that we should + * use when creating requests. + */ + private final SipMessageFactory messageFactory; + /** * The amount of time (in milliseconds) for the initial interval for @@ -140,6 +171,13 @@ public class CallSipImpl } this.retransmitsRingingInterval = retransmitsRingingInterval; + AccountID account = parentOpSet.getProtocolProvider().getAccountID(); + // Specify custom header names + JITSI_MEET_ROOM_HEADER = account.getAccountPropertyString( + JITSI_MEET_ROOM_HEADER_PROPERTY, JITSI_MEET_ROOM_HEADER); + JITSI_MEET_ROOM_PASS_HEADER = account.getAccountPropertyString( + JITSI_MEET_ROOM_PASS_HEADER_PROPERTY, JITSI_MEET_ROOM_PASS_HEADER); + //let's add ourselves to the calls repo. we are doing it ourselves just //to make sure that no one ever forgets. parentOpSet.getActiveCallsRepository().addCall(this); @@ -387,24 +425,21 @@ public class CallSipImpl logger.trace("Looking for peer with dialog: " + dialog + "among " + getCallPeerCount() + " calls"); } - while (callPeers.hasNext()) + for (CallPeerSipImpl callPeer : getCallPeerList()) { - CallPeerSipImpl cp = callPeers.next(); - - if (cp.getDialog() == dialog) + if (callPeer.getDialog() == dialog) { if (logger.isTraceEnabled()) - logger.trace("Returning cp=" + cp); - return cp; + logger.trace("Returning cp=" + callPeer); + return callPeer; } else { if (logger.isTraceEnabled()) - logger.trace("Ignoring cp=" + cp + " because cp.dialog=" - + cp.getDialog() + " while dialog=" + dialog); + logger.trace("Ignoring cp=" + callPeer + " because cp.dialog=" + + callPeer.getDialog() + " while dialog=" + dialog); } } - return null; } @@ -449,7 +484,7 @@ public class CallSipImpl // Transport preference String forceTransport = null; javax.sip.address.URI calleeURI = calleeAddress.getURI(); - if(calleeURI.getScheme().toLowerCase().equals("sips")) + if("sips".equals(calleeURI.getScheme().toLowerCase())) { // MUST use TLS forceTransport = "TLS"; @@ -553,7 +588,7 @@ public class CallSipImpl String alternativeIMPPAddress = null; if (infoHeader != null && infoHeader.getParameter("purpose") != null - && infoHeader.getParameter("purpose").equals("impp")) + && "impp".equals(infoHeader.getParameter("purpose"))) { alternativeIMPPAddress = infoHeader.getInfo().toString(); } @@ -585,7 +620,7 @@ public class CallSipImpl { if (logger.isTraceEnabled()) logger.trace("will send ringing response: "); - if(peer.getState().equals(CallPeerState.INCOMING_CALL)) + if( CallPeerState.INCOMING_CALL.equals(peer.getState()) ) { response = messageFactory.createResponse(Response.RINGING, invite); @@ -680,10 +715,10 @@ public class CallSipImpl */ public void reInvite() throws OperationFailedException { - Iterator<CallPeerSipImpl> peers = getCallPeers(); - - while (peers.hasNext()) - peers.next().sendReInvite(); + for(CallPeerSipImpl peer : getCallPeerList()) + { + peer.sendReInvite(); + } } /** @@ -706,6 +741,22 @@ public class CallSipImpl protected void processExtraHeaders(javax.sip.message.Message message) throws ParseException { + // If there are custom headers added to the call instance, add those + // headers + int extraHeaderIx = 1; + + Object name = getData(EXTRA_HEADER_NAME + "." + extraHeaderIx); + while(name != null) + { + Object value = getData(EXTRA_HEADER_VALUE + "." + extraHeaderIx); + + Header header = getProtocolProvider().getHeaderFactory() + .createHeader((String) name, (String) value); + message.setHeader(header); + + extraHeaderIx++; + name = getData(EXTRA_HEADER_NAME + "." + extraHeaderIx); + } } /** @@ -758,8 +809,8 @@ public class CallSipImpl { try { - if(!peer.getState().equals( - CallPeerState.INCOMING_CALL)) + if( !CallPeerState.INCOMING_CALL.equals( + peer.getState()) ) { timer.cancel(); } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java index f5782f1..3c2aeb5 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java @@ -641,5 +641,31 @@ public class ContactGroupSipImpl return true; } + + @Override + public int hashCode() + { + List<Object> objects = new ArrayList<Object>(); + objects.add(getGroupName()); + objects.add(getUID()); + objects.add(countContacts()); + objects.add(countSubgroups()); + objects.add(getProtocolProvider()); + + //traverse child contacts + for (Contact c : contacts) + { + objects.add(c.getAddress()); + } + + + //traverse subgroups + for (ContactGroup g : subGroups) + { + objects.add(g.getGroupName()); + } + + return Objects.hash(objects.toArray()); + } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java index 0d0b5e8..d4662dd 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java @@ -172,7 +172,7 @@ public class ContactSipImpl public String getAddress() { SipURI sipURI = (SipURI) sipAddress.getURI(); - return sipURI.getUser() + "@" + sipURI.getHost(); + return sipURI.toString().substring(sipURI.getScheme().length() + 1); } /** @@ -535,9 +535,7 @@ public class ContactSipImpl if(obj instanceof String) { String sobj = (String)obj; - - if(sobj.startsWith("sip:")) - sobj = sobj.substring(4); + sobj = stripScheme(stripAddress(sobj)); if(getAddress().equalsIgnoreCase(sobj)) return true; @@ -556,6 +554,43 @@ public class ContactSipImpl } /** + * Get rid of any parameters, ports etc. within a sip contact + * @param address the address to strip + * @return [sip[s]:]user@host without any params or port numbers. + */ + static String stripAddress(String address) + { + if (address != null && address.length() > 0) + { + int idx = address.indexOf(':', 5); + if (idx > -1) + address = address.substring(0, idx); + idx = address.indexOf(';'); + if (idx > -1) + address = address.substring(0, idx); + } + return address; + } + + /** + * @param from address to strip + * @return the address, stripped from either "sip:" or "sips:" + */ + public static String stripScheme(String from) + { + if (from.startsWith("sip:")) + { + return from.substring(4); + } + else if (from.startsWith("sips:")) + { + return from.substring(5); + } + + return from; + } + + /** * Returns the presence operation set that this contact belongs * to. * diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java index 5ee32c9..fc99643 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -434,7 +434,10 @@ public class OperationSetBasicTelephonySipImpl CSeqHeader cseq = ((CSeqHeader) response.getHeader(CSeqHeader.NAME)); if (cseq == null) + { logger.error("An incoming response did not contain a CSeq header"); + return false; + } String method = cseq.getMethod(); @@ -1163,11 +1166,7 @@ public class OperationSetBasicTelephonySipImpl protocolProvider.getAccountID().getService() , 399, reasonText); } - catch(InvalidArgumentException e) - { - logger.error("Cannot create warning header", e); - } - catch(ParseException e) + catch(InvalidArgumentException | ParseException e) { logger.error("Cannot create warning header", e); } @@ -1458,11 +1457,7 @@ public class OperationSetBasicTelephonySipImpl { serverTransaction.sendResponse(accepted); } - catch (InvalidArgumentException ex) - { - failure = ex; - } - catch (SipException ex) + catch (InvalidArgumentException | SipException ex) { failure = ex; } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetJitsiMeetToolsSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetJitsiMeetToolsSipImpl.java index 77a7fae..4997fc1 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetJitsiMeetToolsSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetJitsiMeetToolsSipImpl.java @@ -142,6 +142,16 @@ public class OperationSetJitsiMeetToolsSipImpl * {@inheritDoc} */ @Override + public void removePresenceExtension(ChatRoom chatRoom, + PacketExtension extension) + { + throw new RuntimeException("Not implemented for SIP"); + } + + /** + * {@inheritDoc} + */ + @Override public void setPresenceStatus(ChatRoom chatRoom, String statusMessage) { throw new RuntimeException("Not implemented for SIP"); diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java index 28fce28..7972ee8 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java @@ -209,6 +209,10 @@ public class OperationSetMessageWaitingSipImpl try { subscribeAddress = getSubscribeAddress(); + if (subscribeAddress == null) + { + return; + } } catch (ParseException e) { diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java index 2fce57e..ef0ec4c 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java @@ -2240,6 +2240,40 @@ public class OperationSetPresenceSipImpl updateContactIcon((ContactSipImpl) contact, personStatusIcon); } + // search for a <note> that can define a more precise + // status this is not recommended by RFC3863 but some im + // clients use this. + NodeList presNoteList = getPidfChilds(presence, NOTE_ELEMENT); + if (presNoteList.getLength() >= 1) + { + Node noteNode = presNoteList.item(presNoteList.getLength() - 1); + if (noteNode.getNodeType() == Node.ELEMENT_NODE) + { + String state = getTextContent((Element)noteNode); + if (state != null) + { + switch (state.toLowerCase()) + { + case "ready": + case "available": + personStatus = sipStatusEnum + .getStatus(SipStatusEnum.ONLINE); + break; + case "ringing": + case "on the phone": + case "on hold": + personStatus = sipStatusEnum + .getStatus(SipStatusEnum.ON_THE_PHONE); + break; + case "unavailable": + personStatus = sipStatusEnum + .getStatus(SipStatusEnum.OFFLINE); + break; + } + } + } + } + // Vector containing the list of status to set for each contact in // the presence document ordered by priority (highest first). // <SipContact, Float (priority), SipStatusEnum> diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java index 644c85a..a9aefbe 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java @@ -72,6 +72,10 @@ public class OperationSetTelephonyBLFSipImpl * Account property suffix to set/provision monitored line group. */ public static final String BLF_LINE_GROUP_ACC_PROP_PREFIX = "Group"; + /** + * Account property suffix to set/provision monitored line pickup template. + */ + public static final String BLF_LINE_PICKUP_ACC_PROP_PREFIX = "Pickup"; /** * The name of the event package supported by @@ -195,7 +199,7 @@ public class OperationSetTelephonyBLFSipImpl String[] lineValues = lines.get(ix); if(lineValues == null) { - lineValues = new String[3]; + lineValues = new String[4]; lines.put(ix, lineValues); } @@ -211,13 +215,17 @@ public class OperationSetTelephonyBLFSipImpl { lineValues[2] = entryValue; } + else if(pName.contains(BLF_LINE_PICKUP_ACC_PROP_PREFIX)) + { + lineValues[3] = entryValue; + } } for(Map.Entry<String, String[]> en : lines.entrySet()) { String[] vals = en.getValue(); - this.lines.add(new Line(vals[0], vals[1], vals[2], this.provider)); + this.lines.add(new Line(vals[0], vals[1], vals[2], vals[3], this.provider)); } } @@ -263,16 +271,25 @@ public class OperationSetTelephonyBLFSipImpl if(details == null) return; - if(StringUtils.isNullOrEmpty(details.callID) - || StringUtils.isNullOrEmpty(details.localTag) - || StringUtils.isNullOrEmpty(details.remoteTag)) - return; - // replaces Address targetAddress = null; try { - targetAddress = provider.parseAddressString(line.getAddress()); + String address = line.getAddress(); + if(asteriskMode(details)) + { + // broken mode for Asterisk, doesn't provide us with + // the proper call-id, etc. attributes. + // send an unspecified pickup-call if a template is set + if (StringUtils.isNullOrEmpty(line.getPickupTemplate(), true)) + { + return; + } + + address = line.getPickupTemplate().replace("\\1", address); + } + + targetAddress = provider.parseAddressString(address); } catch (ParseException ex) { @@ -281,6 +298,16 @@ public class OperationSetTelephonyBLFSipImpl OperationFailedException.ILLEGAL_ARGUMENT, ex, logger); } + OperationSetBasicTelephonySipImpl telOpSet + = (OperationSetBasicTelephonySipImpl)provider + .getOperationSet(OperationSetBasicTelephony.class); + + if (asteriskMode(details)) + { + telOpSet.createOutgoingCall(targetAddress, null, null); + return; + } + Replaces replacesHeader = null; SipURI sipURI = (SipURI) targetAddress.getURI(); @@ -315,12 +342,14 @@ public class OperationSetTelephonyBLFSipImpl OperationFailedException.INTERNAL_ERROR, ex, logger); } - OperationSetBasicTelephonySipImpl telOpSet - = (OperationSetBasicTelephonySipImpl)provider - .getOperationSet(OperationSetBasicTelephony.class); - - CallSipImpl call - = telOpSet.createOutgoingCall(targetAddress, null, null); + telOpSet.createOutgoingCall(targetAddress, null, null); + } + + private boolean asteriskMode(LineDetails details) + { + return StringUtils.isNullOrEmpty(details.callID) + || StringUtils.isNullOrEmpty(details.localTag) + || StringUtils.isNullOrEmpty(details.remoteTag); } /** @@ -549,6 +578,8 @@ public class OperationSetTelephonyBLFSipImpl Node dialogNode = dialogList.item(i); Element dialogElem = (Element)dialogNode; + details.id = dialogElem.getAttribute("id"); + details.direction = dialogElem.getAttribute("direction"); details.callID = dialogElem.getAttribute("call-id"); details.localTag = dialogElem.getAttribute("local-tag"); details.remoteTag = dialogElem.getAttribute("remote-tag"); @@ -698,12 +729,25 @@ public class OperationSetTelephonyBLFSipImpl */ private class LineDetails { + /** * The current status of the line, the last event fired for it. */ int lastStatusEvent = BLFStatusEvent.STATUS_OFFLINE; /** + * id of the dialog. Mandatory. + */ + String id = null; + + /** + * either initiator or recipient, and indicates whether the observed + * user was the initiator of the dialog, or the recipient of the INVITE + * that created it. + */ + String direction; + + /** * call-id of the dialog if any, used for remote pickup. */ String callID = null; diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java index 0fbcf23..927c0a3 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java @@ -246,6 +246,76 @@ public class ProtocolProviderServiceSipImpl } /** + * Validates the contact identifier and returns an error message if + * applicable and a suggested correction + * + * @param contactId the contact identifier to validate + * @param result Must be supplied as an empty a list. Implementors add + * items: + * <ol> + * <li>is the error message if applicable + * <li>a suggested correction. Index 1 is optional and can only + * be present if there was a validation failure. + * </ol> + * @return true if the contact id is valid, false otherwise + */ + @Override + public boolean validateContactAddress(String contactId, List<String> result) + { + if (result == null) + { + throw new IllegalArgumentException("result must be an empty list"); + } + + result.clear(); + try + { + Address address = parseAddressString(contactId); + if (address.toString().equals(contactId)) + { + return true; + } + else if (((SipUri) address.getURI()).getUser().equals(contactId)) + { + return true; + } + else if (address.toString().equals(address.getURI().getScheme() + ":" + contactId)) + { + return true; + } + else + { + result.add(SipActivator.getResources().getI18NString( + "impl.protocol.sip.INVALID_ADDRESS", new String[] + { contactId })); + result.add(((SipUri) address.getURI()).getUser()); + } + } + catch (Exception ex) + { + logger.error("Validating SIP address failed for " + contactId, ex); + result.add(SipActivator.getResources() + .getI18NString("impl.protocol.sip.INVALID_ADDRESS", new String[] + { contactId })); + + String user = contactId; + String remainder = ""; + int at = contactId.indexOf('@'); + if (at > -1) + { + user = contactId.substring(0, at); + remainder = contactId.substring(at); + } + + // replace invalid characters in user part with hex encoding + String banned = "([^a-z0-9-_.!~*'()&=+$,;?/])+"; + result.add(user.replaceAll(banned, "") + remainder); + } + + return false; + } + + /** * Indicates whether or not this provider must registered * when placing outgoing calls. * @@ -610,50 +680,50 @@ public class ProtocolProviderServiceSipImpl addSupportedOperationSet( OperationSetPresence.class, opSetPersPresence); + } - // Only init messaging and typing if enabled. - boolean isMessagingDisabled - = SipActivator.getConfigurationService() - .getBoolean(IS_MESSAGING_DISABLED, false); + // Only init messaging and typing if enabled. + boolean isMessagingDisabled + = SipActivator.getConfigurationService() + .getBoolean(IS_MESSAGING_DISABLED, false); - if (!isMessagingDisabled) - { - // init instant messaging - this.opSetBasicIM = - new OperationSetBasicInstantMessagingSipImpl(this); + if (!isMessagingDisabled) + { + // init instant messaging + this.opSetBasicIM = + new OperationSetBasicInstantMessagingSipImpl(this); - addSupportedOperationSet( - OperationSetBasicInstantMessaging.class, - opSetBasicIM); + addSupportedOperationSet( + OperationSetBasicInstantMessaging.class, + opSetBasicIM); - // init typing notifications - this.opSetTypingNotif - = new OperationSetTypingNotificationsSipImpl( - this, opSetBasicIM); - addSupportedOperationSet( - OperationSetTypingNotifications.class, - opSetTypingNotif); + // init typing notifications + this.opSetTypingNotif + = new OperationSetTypingNotificationsSipImpl( + this, opSetBasicIM); + addSupportedOperationSet( + OperationSetTypingNotifications.class, + opSetTypingNotif); - addSupportedOperationSet( - OperationSetInstantMessageTransform.class, - new OperationSetInstantMessageTransformImpl()); - } + addSupportedOperationSet( + OperationSetInstantMessageTransform.class, + new OperationSetInstantMessageTransformImpl()); + } - this.opSetSSAccountInfo = - new OperationSetServerStoredAccountInfoSipImpl(this); + this.opSetSSAccountInfo = + new OperationSetServerStoredAccountInfoSipImpl(this); - // Set the display name. - opSetSSAccountInfo.setOurDisplayName(ourDisplayName); + // Set the display name. + opSetSSAccountInfo.setOurDisplayName(ourDisplayName); - // init avatar - addSupportedOperationSet( - OperationSetServerStoredAccountInfo.class, - opSetSSAccountInfo); + // init avatar + addSupportedOperationSet( + OperationSetServerStoredAccountInfo.class, + opSetSSAccountInfo); - addSupportedOperationSet( - OperationSetAvatar.class, - new OperationSetAvatarSipImpl(this, opSetSSAccountInfo)); - } + addSupportedOperationSet( + OperationSetAvatar.class, + new OperationSetAvatarSipImpl(this, opSetSSAccountInfo)); // MWI is enabled by default if(accountID.getAccountPropertyBoolean( @@ -1519,16 +1589,16 @@ public class ProtocolProviderServiceSipImpl Set<ProtocolProviderServiceSipImpl> instances = new HashSet<ProtocolProviderServiceSipImpl>(); BundleContext context = SipActivator.getBundleContext(); - ServiceReference[] references = context.getServiceReferences( - ProtocolProviderService.class.getName(), - null - ); - for(ServiceReference reference : references) + Collection<ServiceReference<ProtocolProviderService>> references = + context.getServiceReferences(ProtocolProviderService.class, + null); + for(ServiceReference<ProtocolProviderService> ref : references) { - Object service = context.getService(reference); + ProtocolProviderService service = context.getService(ref); if(service instanceof ProtocolProviderServiceSipImpl) instances.add((ProtocolProviderServiceSipImpl) service); } + return instances; } catch(InvalidSyntaxException ex) @@ -2390,21 +2460,38 @@ public class ProtocolProviderServiceSipImpl //we don't know how to handle the "tel:" and "callto:" schemes ... or // rather we handle them same as sip so replace: if(uriStr.toLowerCase().startsWith("tel:")) - uriStr = "sip:" + uriStr.substring("tel:".length()); + uriStr = uriStr.substring("tel:".length()); else if(uriStr.toLowerCase().startsWith("callto:")) - uriStr = "sip:" + uriStr.substring("callto:".length()); + uriStr = uriStr.substring("callto:".length()); + else if(uriStr.toLowerCase().startsWith("sips:")) + uriStr = uriStr.substring("sips:".length()); + else if(uriStr.toLowerCase().startsWith("sip:")) + uriStr = uriStr.substring("sip:".length()); + + String user = uriStr; + String remainder = ""; + int at = uriStr.indexOf('@'); + if (at > -1) + { + user = uriStr.substring(0, at); + remainder = uriStr.substring(at); + } + + //replace invalid characters in user part with hex encoding + String banned = "([^a-z0-9-_.!~*'()&=+$,;?/])+"; + user = user.replaceAll(banned, "") + remainder; //Handle default domain name (i.e. transform 1234 -> 1234@sip.com) //assuming that if no domain name is specified then it should be the //same as ours. - if (uriStr.indexOf('@') == -1) + if (at == -1) { //if we have a registrar, then we could append its domain name as //default SipRegistrarConnection src = sipRegistrarConnection; if(src != null && !src.isRegistrarless() ) { - uriStr = uriStr + "@" + uriStr = user + "@" + ((SipURI)src.getAddressOfRecord().getURI()).getHost(); } @@ -2684,6 +2771,11 @@ public class ProtocolProviderServiceSipImpl */ protected void notifyConnectionFailed() { + if (sipRegistrarConnection.isRegistrarless()) + { + return; + } + if(getRegistrationState().equals(RegistrationState.REGISTERED) && sipRegistrarConnection != null) sipRegistrarConnection.setRegistrationState( diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java b/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java index 5d60612..b6bf997 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java @@ -19,6 +19,7 @@ package net.java.sip.communicator.impl.protocol.sip; import gov.nist.javax.sip.stack.*; +import java.net.*; import java.util.*; import javax.sip.*; @@ -26,6 +27,7 @@ import javax.sip.address.*; import javax.sip.header.*; import javax.sip.message.*; +import net.java.sip.communicator.impl.protocol.sip.net.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.util.*; @@ -137,7 +139,9 @@ public class ProxyRouter ProtocolProviderServiceSipImpl sipProvider = ((ProtocolProviderServiceSipImpl) service); - String proxy = sipProvider.getConnection().getOutboundProxyString(); + final ProxyConnection connection = sipProvider.getConnection(); + final String proxy = connection.getOutboundProxyString(); + logger.trace("Router for proxy: " + proxy); boolean forceLooseRouting = sipProvider.getAccountID() @@ -146,13 +150,30 @@ public class ProxyRouter // P2P case if (proxy == null || forceLooseRouting ) + { + logger.info("Returning default SIP router, P2P/loose routing"); return this.getDefaultRouter(); + } // outbound proxy case Router router = routerCache.get(proxy); if (router == null) { - router = new DefaultRouter(stack, proxy); + router = new DefaultRouter(stack, proxy) + { + @Override + public Hop getNextHop(Request request) throws SipException + { + logger.info("Outbound proxy mode, using proxy " + + proxy + " as hop instead of an address resolved" + + " by the SIP router"); + InetSocketAddress sa = connection.getAddress(); + return new HopImpl( + sa.getAddress().getHostAddress(), + sa.getPort(), + connection.getTransport()); + } + }; routerCache.put(proxy, router); } return router; @@ -164,6 +185,7 @@ public class ProxyRouter logger.error("unable to identify the service which created this " + "out-of-dialog request"); + logger.info("Returning default router"); return this.getDefaultRouter(); } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java index 99cb0f8..2a3ed00 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java @@ -1288,8 +1288,11 @@ public class SipRegistrarConnection if(registrarPort != ListeningPoint.PORT_5060) registrarURI.setPort(registrarPort); - if(!registrationTransport.equals(ListeningPoint.UDP)) + if(!registrationTransport.equals(ListeningPoint.UDP) + && !registrationTransport.equals(ListeningPoint.TLS)) + { registrarURI.setTransportParam(registrationTransport); + } } return registrarURI; } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java b/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java index 2e714b5..c92782e 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java @@ -1172,6 +1172,9 @@ public class SipStackSharing String transport) throws IOException { + logger.info("Gettting source address for " + + localAddress + " -> " + dst + ":" + dstPort + + "(" + transport + ")"); if(ListeningPoint.TLS.equalsIgnoreCase(transport)) return (java.net.InetSocketAddress)(((SipStackImpl)this.stack) .getLocalAddressForTlsDst(dst, dstPort, localAddress)); diff --git a/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java index 885455a..ecec5c5 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java @@ -351,7 +351,11 @@ public class UriHandlerSipImpl // Even if not registered after the timeout, try the call // anyway and the error popup will appear to ask the // user if they want to register - handleUri(uri, provider); + if(provider.getRegistrationState() + != RegistrationState.REGISTERED) + { + handleUri(uri, provider); + } } }, initialRegistrationTimeout); } @@ -401,9 +405,34 @@ public class UriHandlerSipImpl OperationSetBasicTelephony<?> telephonyOpSet = provider.getOperationSet(OperationSetBasicTelephony.class); + OperationSetVideoTelephony videoTelephonyOpSet + = provider.getOperationSet(OperationSetVideoTelephony.class); + + boolean videoCall = false; + if(videoTelephonyOpSet != null + && uri.contains("?")) + { + String params = uri.substring(uri.indexOf('?') + 1); + uri = uri.substring(0, uri.indexOf('?')); + + StringTokenizer paramTokens = new StringTokenizer(params, "&"); + while(paramTokens.hasMoreTokens()) + { + String tok = paramTokens.nextToken(); + String[] keyValue = tok.split("\\="); + if (keyValue.length == 2 + && keyValue[0].equalsIgnoreCase("video") + && keyValue[1].equalsIgnoreCase("true")) + videoCall = true; + } + } + try { - telephonyOpSet.createCall(uri); + if(videoCall) + videoTelephonyOpSet.createVideoCall(uri); + else + telephonyOpSet.createCall(uri); } catch (OperationFailedException exc) { diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java index 5439d99..ca7faa9 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,115 +15,115 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.sip.net;
-
-import static javax.sip.ListeningPoint.PORT_5060;
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PREFERRED_TRANSPORT;
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_ADDRESS;
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_PORT;
-
-import java.net.*;
-import java.text.*;
-
-import net.java.sip.communicator.impl.protocol.sip.*;
-import net.java.sip.communicator.service.dns.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Implementation of the manually configured SIP proxy connection. IP Address
- * lookups are performed using the account's proxy address.
- *
- * @author Ingo Bauersachs
- */
-public class ManualProxyConnection
- extends ProxyConnection
-{
- private final static Logger logger
- = Logger.getLogger(ManualProxyConnection.class);
-
- private String address;
- private int port;
-
- private InetSocketAddress[] lookups;
- private int lookupIndex;
-
- /**
- * Creates a new instance of this class. Uses the server from the account.
- *
- * @param account the account of this SIP protocol instance
- */
- public ManualProxyConnection(SipAccountIDImpl account)
- {
- super(account);
- reset();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#
- * getNextAddress()
- */
- @Override
- public boolean getNextAddressFromDns()
- throws DnssecException
- {
- if(lookups == null)
- {
- try
- {
- lookupIndex = 0;
- lookups = NetworkUtils.getAandAAAARecords(address, port);
-
- //no result found, reset state and indicate "out of addresses"
- if(lookups.length == 0)
- {
- lookups = null;
- return false;
- }
- }
- catch (ParseException e)
- {
- logger.error("Invalid address <" + address + ">", e);
- return false;
- }
- }
-
- //check if the available addresses are exhausted
- if(lookupIndex >= lookups.length)
- {
- if(logger.isDebugEnabled())
- logger.debug("No more addresses for " + account);
- lookups = null;
- return false;
- }
-
- //assign the next address and return lookup success
- if(logger.isDebugEnabled())
- logger.debug("Returning <" + socketAddress
- + "> as next address for " + account);
- socketAddress = lookups[lookupIndex];
- lookupIndex++;
- return true;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#reset()
- */
- @Override
- public void reset()
- {
- super.reset();
- address = account.getAccountPropertyString(PROXY_ADDRESS);
- port = account.getAccountPropertyInt(PROXY_PORT, PORT_5060);
- transport = account.getAccountPropertyString(PREFERRED_TRANSPORT);
-
- //check property sanity
- if(!ProtocolProviderServiceSipImpl.isValidTransport(transport))
- throw new IllegalArgumentException(
- transport + " is not a valid SIP transport");
- }
-}
+package net.java.sip.communicator.impl.protocol.sip.net; + +import static javax.sip.ListeningPoint.PORT_5060; +import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PREFERRED_TRANSPORT; +import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_ADDRESS; +import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_PORT; + +import java.net.*; +import java.text.*; + +import net.java.sip.communicator.impl.protocol.sip.*; +import net.java.sip.communicator.service.dns.*; +import net.java.sip.communicator.util.*; + +/** + * Implementation of the manually configured SIP proxy connection. IP Address + * lookups are performed using the account's proxy address. + * + * @author Ingo Bauersachs + */ +public class ManualProxyConnection + extends ProxyConnection +{ + private final static Logger logger + = Logger.getLogger(ManualProxyConnection.class); + + private String address; + private int port; + + private InetSocketAddress[] lookups; + private int lookupIndex; + + /** + * Creates a new instance of this class. Uses the server from the account. + * + * @param account the account of this SIP protocol instance + */ + public ManualProxyConnection(SipAccountIDImpl account) + { + super(account); + reset(); + } + + /* + * (non-Javadoc) + * + * @see net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection# + * getNextAddress() + */ + @Override + public boolean getNextAddressFromDns() + throws DnssecException + { + if(lookups == null) + { + try + { + lookupIndex = 0; + lookups = NetworkUtils.getAandAAAARecords(address, port); + + //no result found, reset state and indicate "out of addresses" + if(lookups.length == 0) + { + lookups = null; + return false; + } + } + catch (ParseException e) + { + logger.error("Invalid address <" + address + ">", e); + return false; + } + } + + //check if the available addresses are exhausted + if(lookupIndex >= lookups.length) + { + if(logger.isDebugEnabled()) + logger.debug("No more addresses for " + account); + lookups = null; + return false; + } + + //assign the next address and return lookup success + if(logger.isDebugEnabled()) + logger.debug("Returning <" + socketAddress + + "> as next address for " + account); + socketAddress = lookups[lookupIndex]; + lookupIndex++; + return true; + } + + /* + * (non-Javadoc) + * + * @see + * net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#reset() + */ + @Override + public void reset() + { + super.reset(); + address = account.getAccountPropertyString(PROXY_ADDRESS); + port = account.getAccountPropertyInt(PROXY_PORT, PORT_5060); + transport = account.getAccountPropertyString(PREFERRED_TRANSPORT); + + //check property sanity + if(!ProtocolProviderServiceSipImpl.isValidTransport(transport)) + throw new IllegalArgumentException( + transport + " is not a valid SIP transport"); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java index 99fcfb2..f7079f4 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java @@ -1,180 +1,180 @@ -/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package net.java.sip.communicator.impl.protocol.sip.net;
-
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_AUTO_CONFIG;
-
-import java.net.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.sip.*;
-import net.java.sip.communicator.service.dns.*;
-
-/**
- * Abstract class for the determining the address for the SIP proxy.
- *
- * @author Ingo Bauersachs
- */
-public abstract class ProxyConnection
-{
- private List<String> returnedAddresses = new LinkedList<String>();
-
- protected String transport;
- protected InetSocketAddress socketAddress;
- protected final SipAccountIDImpl account;
-
- /**
- * Creates a new instance of this class.
- * @param account the account of this SIP protocol instance
- */
- protected ProxyConnection(SipAccountIDImpl account)
- {
- this.account = account;
- }
-
- /**
- * Gets the address to use for the next connection attempt.
- * @return the address of the last lookup.
- */
- public final InetSocketAddress getAddress()
- {
- return socketAddress;
- }
-
- /**
- * Gets the transport to use for the next connection attempt.
- * @return the transport of the last lookup.
- */
- public final String getTransport()
- {
- return transport;
- }
-
- /**
- * In case we are using an outbound proxy this method returns
- * a suitable string for use with Router.
- * The method returns <tt>null</tt> otherwise.
- *
- * @return the string of our outbound proxy if we are using one and
- * <tt>null</tt> otherwise.
- */
- public final String getOutboundProxyString()
- {
- if(socketAddress == null)
- return null;
-
- InetAddress proxyAddress = socketAddress.getAddress();
- StringBuilder proxyStringBuffer
- = new StringBuilder(proxyAddress.getHostAddress());
-
- if(proxyAddress instanceof Inet6Address)
- {
- proxyStringBuffer.insert(0, '[');
- proxyStringBuffer.append(']');
- }
-
- proxyStringBuffer.append(':');
- proxyStringBuffer.append(socketAddress.getPort());
- proxyStringBuffer.append('/');
- proxyStringBuffer.append(transport);
-
- return proxyStringBuffer.toString();
- }
-
- /**
- * Compares an InetAddress against the active outbound proxy. The comparison
- * is by reference, not equals.
- *
- * @param addressToTest The addres to test.
- * @return True when the InetAddress is the same as the outbound proxy.
- */
- public final boolean isSameInetAddress(InetAddress addressToTest)
- {
- // if the proxy is not yet initialized then this is not the provider
- // that caused this comparison
- if(socketAddress == null)
- return false;
- return addressToTest == socketAddress.getAddress();
- }
-
- /**
- * Retrieves the next address to use from DNS. Duplicate results are
- * suppressed.
- *
- * @return True if a new address is available through {@link #getAddress()},
- * false if the last address was reached. A new lookup from scratch
- * can be started by calling {@link #reset()}.
- * @throws DnssecException if there is a problem related to DNSSEC
- */
- public final boolean getNextAddress() throws DnssecException
- {
- boolean result;
- String key = null;
- do
- {
- result = getNextAddressFromDns();
- if(result && socketAddress != null)
- {
- key = getOutboundProxyString();
- if(!returnedAddresses.contains(key))
- {
- returnedAddresses.add(key);
- break;
- }
- }
- }
- while(result && returnedAddresses.contains(key));
- return result;
- }
-
- /**
- * Implementations must use this method to get the next address, but do not
- * have to care about duplicate addresses.
- *
- * @return True when a further address was available.
- * @throws DnssecException when a DNSSEC validation failure occured.
- */
- protected abstract boolean getNextAddressFromDns()
- throws DnssecException;
-
- /**
- * Resets the lookup to it's initial state. Overriders methods have to call
- * this method through a super-call.
- */
- public void reset()
- {
- returnedAddresses.clear();
- }
-
- /**
- * Factory method to create a proxy connection based on the account settings
- * of the protocol provider.
- *
- * @param pps the protocol provider that needs a SIP server connection.
- * @return An instance of a derived class.
- */
- public static ProxyConnection create(ProtocolProviderServiceSipImpl pps)
- {
- if (pps.getAccountID().getAccountPropertyBoolean(PROXY_AUTO_CONFIG,
- true))
- return new AutoProxyConnection((SipAccountIDImpl) pps.getAccountID(),
- pps.getDefaultTransport());
- else
- return new ManualProxyConnection((SipAccountIDImpl) pps.getAccountID());
- }
-}
+/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Copyright @ 2015 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.java.sip.communicator.impl.protocol.sip.net; + +import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_AUTO_CONFIG; + +import java.net.*; +import java.util.*; + +import net.java.sip.communicator.impl.protocol.sip.*; +import net.java.sip.communicator.service.dns.*; + +/** + * Abstract class for the determining the address for the SIP proxy. + * + * @author Ingo Bauersachs + */ +public abstract class ProxyConnection +{ + private List<String> returnedAddresses = new LinkedList<String>(); + + protected String transport; + protected InetSocketAddress socketAddress; + protected final SipAccountIDImpl account; + + /** + * Creates a new instance of this class. + * @param account the account of this SIP protocol instance + */ + protected ProxyConnection(SipAccountIDImpl account) + { + this.account = account; + } + + /** + * Gets the address to use for the next connection attempt. + * @return the address of the last lookup. + */ + public final InetSocketAddress getAddress() + { + return socketAddress; + } + + /** + * Gets the transport to use for the next connection attempt. + * @return the transport of the last lookup. + */ + public final String getTransport() + { + return transport; + } + + /** + * In case we are using an outbound proxy this method returns + * a suitable string for use with Router. + * The method returns <tt>null</tt> otherwise. + * + * @return the string of our outbound proxy if we are using one and + * <tt>null</tt> otherwise. + */ + public final String getOutboundProxyString() + { + if(socketAddress == null) + return null; + + InetAddress proxyAddress = socketAddress.getAddress(); + StringBuilder proxyStringBuffer + = new StringBuilder(proxyAddress.getHostAddress()); + + if(proxyAddress instanceof Inet6Address) + { + proxyStringBuffer.insert(0, '['); + proxyStringBuffer.append(']'); + } + + proxyStringBuffer.append(':'); + proxyStringBuffer.append(socketAddress.getPort()); + proxyStringBuffer.append('/'); + proxyStringBuffer.append(transport); + + return proxyStringBuffer.toString(); + } + + /** + * Compares an InetAddress against the active outbound proxy. The comparison + * is by reference, not equals. + * + * @param addressToTest The addres to test. + * @return True when the InetAddress is the same as the outbound proxy. + */ + public final boolean isSameInetAddress(InetAddress addressToTest) + { + // if the proxy is not yet initialized then this is not the provider + // that caused this comparison + if(socketAddress == null) + return false; + return addressToTest == socketAddress.getAddress(); + } + + /** + * Retrieves the next address to use from DNS. Duplicate results are + * suppressed. + * + * @return True if a new address is available through {@link #getAddress()}, + * false if the last address was reached. A new lookup from scratch + * can be started by calling {@link #reset()}. + * @throws DnssecException if there is a problem related to DNSSEC + */ + public final boolean getNextAddress() throws DnssecException + { + boolean result; + String key = null; + do + { + result = getNextAddressFromDns(); + if(result && socketAddress != null) + { + key = getOutboundProxyString(); + if(!returnedAddresses.contains(key)) + { + returnedAddresses.add(key); + break; + } + } + } + while(result && returnedAddresses.contains(key)); + return result; + } + + /** + * Implementations must use this method to get the next address, but do not + * have to care about duplicate addresses. + * + * @return True when a further address was available. + * @throws DnssecException when a DNSSEC validation failure occured. + */ + protected abstract boolean getNextAddressFromDns() + throws DnssecException; + + /** + * Resets the lookup to it's initial state. Overriders methods have to call + * this method through a super-call. + */ + public void reset() + { + returnedAddresses.clear(); + } + + /** + * Factory method to create a proxy connection based on the account settings + * of the protocol provider. + * + * @param pps the protocol provider that needs a SIP server connection. + * @return An instance of a derived class. + */ + public static ProxyConnection create(ProtocolProviderServiceSipImpl pps) + { + if (pps.getAccountID().getAccountPropertyBoolean(PROXY_AUTO_CONFIG, + true)) + return new AutoProxyConnection((SipAccountIDImpl) pps.getAccountID(), + pps.getDefaultTransport()); + else + return new ManualProxyConnection((SipAccountIDImpl) pps.getAccountID()); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java index 0027ec5..262b717 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,211 +15,211 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.sip.net;
-
-import java.security.cert.*;
-import java.text.*;
-import java.util.*;
-import java.util.regex.*;
-
-import javax.sip.address.*;
-
-import net.java.sip.communicator.impl.protocol.sip.*;
-import net.java.sip.communicator.service.certificate.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Matcher that extracts certificate identities according to <a
- * href="http://tools.ietf.org/html/rfc5922#section-7.1">RFC5922, Section
- * 7.1</a> and compares them with the rules from Section 7.2 and 7.3.
- * @see #PNAME_STRICT_RFC5922 for wildcard handling; the default is false
- *
- * @author Ingo Bauersachs
- */
-public class RFC5922Matcher
- implements CertificateMatcher
-{
- /**
- * When set to true, enables strict validation of the hostname according to
- * <a href="http://tools.ietf.org/html/rfc5922#section-7.2">RFC5922 Section
- * 7.2</a>
- */
- public final static String PNAME_STRICT_RFC5922 =
- "net.java.sip.communicator.sip.tls.STRICT_RFC5922";
-
- private ProtocolProviderServiceSipImpl provider;
-
- /**
- * Creates a new instance of this class.
- * @param provider The SIP Provider to which this matcher belongs.
- */
- public RFC5922Matcher(ProtocolProviderServiceSipImpl provider)
- {
- this.provider = provider;
- }
-
- /** Our class logger. */
- private static final Logger logger = Logger
- .getLogger(CertificateMatcher.class);
-
- /*
- * (non-Javadoc)
- *
- * @see
- * net.java.sip.communicator.service.certificate.CertificateMatcher#verify
- * (java.lang.Iterable, java.security.cert.X509Certificate)
- */
- public void verify(Iterable<String> identitiesToTest, X509Certificate cert)
- throws CertificateException
- {
- boolean strict = SipActivator.getConfigurationService()
- .getBoolean(PNAME_STRICT_RFC5922, false);
-
- // if any of the identities is contained in the certificate we're good
- boolean oneMatched = false;
- Iterable<String> certIdentities = extractCertIdentities(cert);
- for (String identity : identitiesToTest)
- {
- // check if the intended hostname is contained in one of the
- // hostnames of the certificate according to
- // http://tools.ietf.org/html/rfc5922#section-7.2
- for(String dnsName : certIdentities)
- {
- try
- {
- if(NetworkUtils.compareDnsNames(dnsName, identity) == 0)
- {
- // one of the hostnames matched, we're good to go
- return;
- }
-
- if(!strict
- // is a wildcard name
- && dnsName.startsWith("*.")
- // contains at least two dots (*.example.com)
- && identity.indexOf(".") < identity.lastIndexOf(".")
- // compare *.example.com stripped to example.com with
- // - foo.example.com stripped to example.com
- // - foo.bar.example.com to bar.example.com
- && NetworkUtils.compareDnsNames(
- dnsName.substring(2),
- identity.substring(identity.indexOf(".")+1)) == 0)
- {
- // the wildcard matched, we're good to go
- return;
- }
- }
- catch (ParseException e)
- {} // we don't care - this hostname did not match
- }
- }
- if (!oneMatched)
- throw new CertificateException("None of <" + identitiesToTest
- + "> matched by the rules of RFC5922 to the cert with CN="
- + cert.getSubjectDN());
- }
-
- private Iterable<String> extractCertIdentities(X509Certificate cert)
- {
- List<String> certIdentities = new ArrayList<String>();
- Collection<List<?>> subjAltNames = null;
- try
- {
- subjAltNames = cert.getSubjectAlternativeNames();
- }
- catch (CertificateParsingException ex)
- {
- logger.error("Error parsing TLS certificate", ex);
- }
- // subjAltName types are defined in rfc2459
- final Integer dnsNameType = 2;
- final Integer uriNameType = 6;
- if (subjAltNames != null)
- {
- if (logger.isDebugEnabled())
- logger.debug("found subjAltNames: " + subjAltNames);
-
- // First look for a URI in the subjectAltName field
- for (List<?> altName : subjAltNames)
- {
- // 0th position is the alt name type
- // 1st position is the alt name data
- if (altName.get(0).equals(uriNameType))
- {
- SipURI altNameUri;
- try
- {
- altNameUri =
- provider.getAddressFactory().createSipURI(
- (String) altName.get(1));
- // only sip URIs are allowed
- if (!"sip".equals(altNameUri.getScheme()))
- continue;
- // user certificates are not allowed
- if (altNameUri.getUser() != null)
- continue;
- String altHostName = altNameUri.getHost();
- if (logger.isDebugEnabled())
- {
- logger.debug("found uri " + altName.get(1)
- + ", hostName " + altHostName);
- }
- certIdentities.add(altHostName);
- }
- catch (ParseException e)
- {
- logger.error("certificate contains invalid uri: "
- + altName.get(1));
- }
- }
-
- }
- // DNS An implementation MUST accept a domain name system
- // identifier as a SIP domain identity if and only if no other
- // identity is found that matches the "sip" URI type described
- // above.
- if (certIdentities.isEmpty())
- {
- for (List<?> altName : subjAltNames)
- {
- if (altName.get(0).equals(dnsNameType))
- {
- if (logger.isDebugEnabled())
- logger.debug("found dns " + altName.get(1));
- certIdentities.add(altName.get(1).toString());
- }
- }
- }
- }
- else
- {
- // If and only if the subjectAltName does not appear in the
- // certificate, the implementation MAY examine the CN field of the
- // certificate. If a valid DNS name is found there, the
- // implementation MAY accept this value as a SIP domain identity.
- String dname = cert.getSubjectDN().getName();
- String cname = "";
- try
- {
- Pattern EXTRACT_CN =
- Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*");
- Matcher matcher = EXTRACT_CN.matcher(dname);
- if (matcher.matches())
- {
- cname = matcher.group(1);
- if (logger.isDebugEnabled())
- {
- logger.debug("found CN: " + cname + " from DN: "
- + dname);
- }
- certIdentities.add(cname);
- }
- }
- catch (Exception ex)
- {
- logger.error("exception while extracting CN", ex);
- }
- }
- return certIdentities;
- }
-}
+package net.java.sip.communicator.impl.protocol.sip.net; + +import java.security.cert.*; +import java.text.*; +import java.util.*; +import java.util.regex.*; + +import javax.sip.address.*; + +import net.java.sip.communicator.impl.protocol.sip.*; +import net.java.sip.communicator.service.certificate.*; +import net.java.sip.communicator.util.*; + +/** + * Matcher that extracts certificate identities according to <a + * href="http://tools.ietf.org/html/rfc5922#section-7.1">RFC5922, Section + * 7.1</a> and compares them with the rules from Section 7.2 and 7.3. + * @see #PNAME_STRICT_RFC5922 for wildcard handling; the default is false + * + * @author Ingo Bauersachs + */ +public class RFC5922Matcher + implements CertificateMatcher +{ + /** + * When set to true, enables strict validation of the hostname according to + * <a href="http://tools.ietf.org/html/rfc5922#section-7.2">RFC5922 Section + * 7.2</a> + */ + public final static String PNAME_STRICT_RFC5922 = + "net.java.sip.communicator.sip.tls.STRICT_RFC5922"; + + private ProtocolProviderServiceSipImpl provider; + + /** + * Creates a new instance of this class. + * @param provider The SIP Provider to which this matcher belongs. + */ + public RFC5922Matcher(ProtocolProviderServiceSipImpl provider) + { + this.provider = provider; + } + + /** Our class logger. */ + private static final Logger logger = Logger + .getLogger(CertificateMatcher.class); + + /* + * (non-Javadoc) + * + * @see + * net.java.sip.communicator.service.certificate.CertificateMatcher#verify + * (java.lang.Iterable, java.security.cert.X509Certificate) + */ + public void verify(Iterable<String> identitiesToTest, X509Certificate cert) + throws CertificateException + { + boolean strict = SipActivator.getConfigurationService() + .getBoolean(PNAME_STRICT_RFC5922, false); + + // if any of the identities is contained in the certificate we're good + boolean oneMatched = false; + Iterable<String> certIdentities = extractCertIdentities(cert); + for (String identity : identitiesToTest) + { + // check if the intended hostname is contained in one of the + // hostnames of the certificate according to + // http://tools.ietf.org/html/rfc5922#section-7.2 + for(String dnsName : certIdentities) + { + try + { + if(NetworkUtils.compareDnsNames(dnsName, identity) == 0) + { + // one of the hostnames matched, we're good to go + return; + } + + if(!strict + // is a wildcard name + && dnsName.startsWith("*.") + // contains at least two dots (*.example.com) + && identity.indexOf(".") < identity.lastIndexOf(".") + // compare *.example.com stripped to example.com with + // - foo.example.com stripped to example.com + // - foo.bar.example.com to bar.example.com + && NetworkUtils.compareDnsNames( + dnsName.substring(2), + identity.substring(identity.indexOf(".")+1)) == 0) + { + // the wildcard matched, we're good to go + return; + } + } + catch (ParseException e) + {} // we don't care - this hostname did not match + } + } + if (!oneMatched) + throw new CertificateException("None of <" + identitiesToTest + + "> matched by the rules of RFC5922 to the cert with CN=" + + cert.getSubjectDN()); + } + + private Iterable<String> extractCertIdentities(X509Certificate cert) + { + List<String> certIdentities = new ArrayList<String>(); + Collection<List<?>> subjAltNames = null; + try + { + subjAltNames = cert.getSubjectAlternativeNames(); + } + catch (CertificateParsingException ex) + { + logger.error("Error parsing TLS certificate", ex); + } + // subjAltName types are defined in rfc2459 + final Integer dnsNameType = 2; + final Integer uriNameType = 6; + if (subjAltNames != null) + { + if (logger.isDebugEnabled()) + logger.debug("found subjAltNames: " + subjAltNames); + + // First look for a URI in the subjectAltName field + for (List<?> altName : subjAltNames) + { + // 0th position is the alt name type + // 1st position is the alt name data + if (altName.get(0).equals(uriNameType)) + { + SipURI altNameUri; + try + { + altNameUri = + provider.getAddressFactory().createSipURI( + (String) altName.get(1)); + // only sip URIs are allowed + if (!"sip".equals(altNameUri.getScheme())) + continue; + // user certificates are not allowed + if (altNameUri.getUser() != null) + continue; + String altHostName = altNameUri.getHost(); + if (logger.isDebugEnabled()) + { + logger.debug("found uri " + altName.get(1) + + ", hostName " + altHostName); + } + certIdentities.add(altHostName); + } + catch (ParseException e) + { + logger.error("certificate contains invalid uri: " + + altName.get(1)); + } + } + + } + // DNS An implementation MUST accept a domain name system + // identifier as a SIP domain identity if and only if no other + // identity is found that matches the "sip" URI type described + // above. + if (certIdentities.isEmpty()) + { + for (List<?> altName : subjAltNames) + { + if (altName.get(0).equals(dnsNameType)) + { + if (logger.isDebugEnabled()) + logger.debug("found dns " + altName.get(1)); + certIdentities.add(altName.get(1).toString()); + } + } + } + } + else + { + // If and only if the subjectAltName does not appear in the + // certificate, the implementation MAY examine the CN field of the + // certificate. If a valid DNS name is found there, the + // implementation MAY accept this value as a SIP domain identity. + String dname = cert.getSubjectDN().getName(); + String cname = ""; + try + { + Pattern EXTRACT_CN = + Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*"); + Matcher matcher = EXTRACT_CN.matcher(dname); + if (matcher.matches()) + { + cname = matcher.group(1); + if (logger.isDebugEnabled()) + { + logger.debug("found CN: " + cname + " from DN: " + + dname); + } + certIdentities.add(cname); + } + } + catch (Exception ex) + { + logger.error("exception while extracting CN", ex); + } + } + return certIdentities; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java b/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java index f1c5856..9d088a2 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java @@ -18,6 +18,7 @@ package net.java.sip.communicator.impl.protocol.sip.net; import gov.nist.core.net.*; +import gov.nist.javax.sip.*; import java.io.*; import java.net.*; @@ -421,4 +422,9 @@ public class SslNetworkLayer return 0; } + + @Override + public void setSipStack(SipStackImpl sipStack) + { + } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java index c51a96c..e30b12d 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java @@ -36,6 +36,7 @@ import org.jitsi.service.neomedia.*; import org.jitsi.service.neomedia.MediaType; import org.jitsi.service.neomedia.format.*; import org.jitsi.util.*; +import org.opentelecoms.javax.sdp.*; /** * The class contains a number of utility methods that are meant to facilitate @@ -64,7 +65,7 @@ public class SdpUtils /** * A reference to the currently valid SDP factory instance. */ - private static final SdpFactory sdpFactory = SdpFactory.getInstance(); + private static final SdpFactory sdpFactory = new NistSdpFactory(); /** * The name of the SDP attribute that defines zrtp hello hash. @@ -455,8 +456,21 @@ public class SdpUtils while (iter.hasNext()) { Map.Entry<String, String> ntry = iter.next(); - Attribute adv = sdpFactory.createAttribute(ntry.getKey(), - payloadType + " " + ntry.getValue()); + Attribute adv; + switch (ntry.getKey()) + { + // RFC7587, Sect. 7 says there's no payload number for ptime + case "ptime": + case "maxptime": + adv = sdpFactory.createAttribute(ntry.getKey(), + ntry.getValue()); + break; + default: + adv = sdpFactory.createAttribute(ntry.getKey(), + payloadType + " " + ntry.getValue()); + break; + } + mediaAttributes.add(adv); } @@ -567,7 +581,7 @@ public class SdpUtils public static SessionDescription createSessionDescription( InetAddress localAddress, String userName, - Vector<MediaDescription> mediaDescriptions) + List<MediaDescription> mediaDescriptions) throws OperationFailedException { SessionDescription sessDescr = null; @@ -613,8 +627,9 @@ public class SdpUtils sessDescr.setConnection(c); if ( mediaDescriptions != null) - sessDescr.setMediaDescriptions(mediaDescriptions); - + { + sessDescr.setMediaDescriptions( new Vector<>(mediaDescriptions)); + } return sessDescr; } catch (SdpException exc) @@ -655,7 +670,7 @@ public class SdpUtils public static SessionDescription createSessionUpdateDescription( SessionDescription descToUpdate, InetAddress newConnectionAddress, - Vector<MediaDescription> newMediaDescriptions) + List<MediaDescription> newMediaDescriptions) throws OperationFailedException { SessionDescription update = createSessionDescription( @@ -1711,7 +1726,7 @@ public class SdpUtils * <tt>descs</tt> <tt>Vector</tt>. */ private static MediaDescription removeMediaDesc( - Vector<MediaDescription> descs, + List<MediaDescription> descs, MediaType type) { for (Iterator<MediaDescription> i = descs.iterator(); i.hasNext();) diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf index c15c025..2015a17 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf @@ -5,9 +5,22 @@ Bundle-Vendor: jitsi.org Bundle-Version: 0.0.1 Bundle-SymbolicName: net.java.sip.communicator.protocol.sip Import-Package: ch.imvs.sdes4j.srtp, + gov.nist.core, + gov.nist.core.net, + gov.nist.javax.sip, + gov.nist.javax.sip.address, + gov.nist.javax.sip.header, + gov.nist.javax.sip.header.extensions, + gov.nist.javax.sip.message, + gov.nist.javax.sip.stack, javax.net, javax.net.ssl, javax.security.auth.x500, + javax.sdp, + javax.sip, + javax.sip.address, + javax.sip.header, + javax.sip.message, javax.xml.datatype, javax.xml.namespace, javax.xml.parsers, @@ -76,6 +89,7 @@ Import-Package: ch.imvs.sdes4j.srtp, org.jitsi.util.xml, org.json.simple, org.osgi.framework, + org.opentelecoms.javax.sdp, org.w3c.dom, org.xml.sax Export-Package: net.java.sip.communicator.impl.protocol.sip, @@ -88,6 +102,4 @@ Export-Package: net.java.sip.communicator.impl.protocol.sip, net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists, net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps, net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror, - net.java.sip.communicator.impl.protocol.sip.xcap.utils, - javax.sdp, - gov.nist.javax.sdp.fields + net.java.sip.communicator.impl.protocol.sip.xcap.utils diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ContactGroupSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ContactGroupSSHImpl.java deleted file mode 100644 index 7de2e16..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/ContactGroupSSHImpl.java +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * A simple, straightforward implementation of a ssh ContactGroup. Since - * the SSH 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 Shobhit Jindal - */ -public class ContactGroupSSHImpl - implements ContactGroup -{ - - /** - * The name of this SSH contact group. - */ - private String groupName = null; - - /** - * The list of this group's members. - */ - private Vector<Contact> contacts = new Vector<Contact>(); - - /** - * The list of sub groups belonging to this group. - */ - private Vector<ContactGroup> subGroups = new Vector<ContactGroup>(); - - /** - * The group that this group belongs to (or null if this is the root group). - */ - private ContactGroupSSHImpl 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 ProtocolProviderServiceSSHImpl 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 ContactGroupSSHImpl with the specified name. - * - * @param groupName the name of the group. - * @param parentProvider the protocol provider that created this group. - */ - public ContactGroupSSHImpl( - String groupName, - ProtocolProviderServiceSSHImpl 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<Contact> contacts() - { - return contacts.iterator(); - } - - /** - * Adds the specified contact to this group. - * @param contactToAdd the ContactSSHImpl to add to this group. - */ - public void addContact(ContactSSH 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 ContactGroupSSHImpl to add as a subgroup to this - * group. - */ - public void addSubgroup(ContactGroupSSHImpl subgroup) - { - this.subGroups.add(subgroup); - subgroup.setParentGroup(this); - } - - /** - * Sets the group that is the new parent of this group - * @param parent ContactGroupSSHImpl - */ - void setParentGroup(ContactGroupSSHImpl 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 ContactGroupSSHImpl subgroup to remove. - */ - public void removeSubGroup(ContactGroupSSHImpl subgroup) - { - this.subGroups.remove(subgroup); - subgroup.setParentGroup(null); - } - - /** - * Returns the group that is parent of the specified sshGroup or null - * if no parent was found. - * @param sshGroup the group whose parent we're looking for. - * @return the ContactGroupSSHImpl instance that sshGroup - * belongs to or null if no parent was found. - */ - public ContactGroupSSHImpl findGroupParent( - ContactGroupSSHImpl sshGroup) - { - if ( subGroups.contains(sshGroup) ) - return this; - - Iterator<ContactGroup> subGroupsIter = subgroups(); - while (subGroupsIter.hasNext()) - { - ContactGroupSSHImpl subgroup - = (ContactGroupSSHImpl) subGroupsIter.next(); - - ContactGroupSSHImpl parent - = subgroup.findGroupParent(sshGroup); - - if(parent != null) - return parent; - } - return null; - } - - /** - * Returns the group that is parent of the specified sshContact or - * null if no parent was found. - * - * @param sshContact the contact whose parent we're looking for. - * @return the ContactGroupSSHImpl instance that sshContact - * belongs to or <tt>null</tt> if no parent was found. - */ - public ContactGroupSSHImpl findContactParent( - ContactSSHImpl sshContact) - { - if ( contacts.contains(sshContact) ) - return this; - - Iterator<ContactGroup> subGroupsIter = subgroups(); - while (subGroupsIter.hasNext()) - { - ContactGroupSSHImpl subgroup - = (ContactGroupSSHImpl) subGroupsIter.next(); - - ContactGroupSSHImpl parent - = subgroup.findContactParent(sshContact); - - 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<Contact> contactsIter = contacts(); - while (contactsIter.hasNext()) - { - ContactSSHImpl contact = (ContactSSHImpl) 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 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<ContactGroup> groupsIter = subgroups(); - while (groupsIter.hasNext()) - { - ContactGroupSSHImpl contactGroup - = (ContactGroupSSHImpl) 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<ContactGroup> subgroups() - { - return subGroups.iterator(); - } - - /** - * Removes the specified contact from this group. - * @param contact the ContactSSHImpl to remove from this group - */ - public void removeContact(ContactSSHImpl 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 ContactSSHImpl - */ - public ContactSSHImpl findContactByID(String id) - { - //first go through the contacts that are direct children. - Iterator<Contact> contactsIter = contacts(); - - while(contactsIter.hasNext()) - { - ContactSSHImpl mContact = (ContactSSHImpl)contactsIter.next(); - - if( mContact.getAddress().equals(id) ) - return mContact; - } - - //if we didn't find it here, let's try in the subougroups - Iterator<ContactGroup> groupsIter = subgroups(); - - while( groupsIter.hasNext() ) - { - ContactGroupSSHImpl mGroup = (ContactGroupSSHImpl)groupsIter.next(); - - ContactSSHImpl 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. - */ - @Override - public String toString() - { - StringBuffer buff = new StringBuffer(getGroupName()); - buff.append(".subGroups=" + countSubgroups() + ":\n"); - - Iterator<ContactGroup> subGroups = subgroups(); - while (subGroups.hasNext()) - { - ContactGroup group = subGroups.next(); - buff.append(group.toString()); - if (subGroups.hasNext()) - buff.append("\n"); - } - - buff.append("\nChildContacts="+countContacts()+":["); - - Iterator<Contact> contacts = contacts(); - while (contacts.hasNext()) - { - Contact contact = 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. - */ - @Override - public boolean equals(Object obj) - { - if(obj == null - || !(obj instanceof ContactGroupSSHImpl)) - return false; - - ContactGroupSSHImpl sshGroup - = (ContactGroupSSHImpl)obj; - - if( ! sshGroup.getGroupName().equals(getGroupName()) - || ! sshGroup.getUID().equals(getUID()) - || sshGroup.countContacts() != countContacts() - || sshGroup.countSubgroups() != countSubgroups()) - return false; - - //traverse child contacts - Iterator<Contact> theirContacts = sshGroup.contacts(); - - while(theirContacts.hasNext()) - { - Contact theirContact = theirContacts.next(); - Contact ourContact = getContact(theirContact.getAddress()); - - if(ourContact == null - || !ourContact.equals(theirContact)) - return false; - } - - //traverse subgroups - Iterator<ContactGroup> theirSubgroups = sshGroup.subgroups(); - - while(theirSubgroups.hasNext()) - { - ContactGroup theirSubgroup = theirSubgroups.next(); - ContactGroup ourSubgroup = getGroup(theirSubgroup.getGroupName()); - - if(ourSubgroup == null - || !ourSubgroup.equals(theirSubgroup)) - return false; - } - - return true; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSH.java b/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSH.java deleted file mode 100644 index a13414b..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSH.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; - -import net.java.sip.communicator.service.protocol.*; - -import com.jcraft.jsch.*; - -/** - * This interface represents a Contact of SSH Type - * As a SSH Session is specific to a contact, additional information needed - * to maintain its state with the remote server is present here - * - * @author Shobhit Jindal - */ -interface ContactSSH - extends Contact -{ - /** - * An event type indicating that the message being received is a standard - * conversation message sent by another contact. - */ - public static final int CONVERSATION_MESSAGE_RECEIVED = 1; - - /** - * An event type indicting that the message being received is a system - * message being sent by the server or a system administrator. - */ - public static final int SYSTEM_MESSAGE_RECEIVED = 2; - - //Following eight function declations to be moved to Contact - - /** - * This method is only called when the contact is added to a new - * <tt>ContactGroupSSHImpl</tt> by the - * <tt>ContactGroupSSHImpl</tt> itself. - * - * @param newParentGroup the <tt>ContactGroupSSHImpl</tt> that is now - * parent of this <tt>ContactSSHImpl</tt> - */ - void setParentGroup (ContactGroupSSHImpl newParentGroup); - - /** - * Sets <tt>sshPresenceStatus</tt> as the PresenceStatus that this - * contact is currently in. - * @param sshPresenceStatus the <tt>SSHPresenceStatus</tt> - * currently valid for this contact. - */ - public void setPresenceStatus (PresenceStatus sshPresenceStatus); - - /** - * Returns the persistent presence operation set that this contact belongs - * to. - * - * @return the <tt>OperationSetPersistentPresenceSSHImpl</tt> that - * this contact belongs to. - */ - public OperationSetPersistentPresence - getParentPresenceOperationSet (); - - /** - * Returns the BasicInstant Messaging operation set that this contact - * belongs to. - * - * @return the <tt>OperationSetBasicInstantMessagingSSHImpl</tt> that - * this contact belongs to. - */ - public OperationSetBasicInstantMessaging - getParentBasicInstantMessagingOperationSet (); - - /** - * Returns the File Transfer operation set that this contact belongs - * to. - * - * @return the <tt>OperationSetFileTransferSSHImpl</tt> that - * this contact belongs to. - */ - public OperationSetFileTransfer - getFileTransferOperationSet (); - - /** - * Return the type of message received from remote server - * - * @return messageType - */ - public int getMessageType (); - - /** - * Sets the type of message received from remote server - * - * @param messageType - */ - public void setMessageType (int messageType); - - /** - * Stores persistent data of the contact. - * - * @param persistentData of the contact - */ - public void setPersistentData (String persistentData); - - /** - * Makes the contact resolved or unresolved. - * - * @param resolved true to make the contact resolved; false to - * make it unresolved - */ - public void setResolved (boolean resolved); - - /** - * 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); - - /** - * Returns true if a command has been sent whos reply was not received yet - * false otherwise - * - * @return commandSent - */ - public boolean isCommandSent (); - - /** - * Set the state of commandSent variable which determines whether a reply - * to a command sent is awaited - * - * @param commandSent - */ - public void setCommandSent (boolean commandSent); - - /** - * Initializes the reader and writers associated with shell of this contact - * - * @param shellInputStream The InputStream of stack - * @param shellOutputStream The OutputStream of stack - */ - void initializeShellIO (InputStream shellInputStream, - OutputStream shellOutputStream); - - /** - * Closes the readers and writer associated with shell of this contact - */ - void closeShellIO (); - - /** - * Determines whether a connection to a remote server is already underway - * - * @return connectionInProgress - */ - public boolean isConnectionInProgress (); - - /** - * Sets the status of connection attempt to remote server - * - * @param connectionInProgress - */ - public void setConnectionInProgress (boolean connectionInProgress); - -// /** -// * Sets the PS1 prompt of the current shell of Contact -// * This method is synchronized -// * -// * @param sshPrompt to be associated -// */ -// public void setShellPrompt(String sshPrompt); -// -// /** -// * Returns the PS1 prompt of the current shell of Contact -// * -// * @return sshPrompt -// */ -// public String getShellPrompt(); - - - /** - * Saves the details of contact in persistentData - */ - public void savePersistentDetails (); - - /* - * Returns the SSHContactInfo associated with this contact - * - * @return sshConfigurationForm - */ - public SSHContactInfo getSSHConfigurationForm (); - - /** - * Returns the JSch Stack identified associated with this contact - * - * @return jsch - */ - JSch getJSch (); - - /** - * Starts the timer and its task to periodically update the status of - * remote machine - */ - void startTimerTask (); - - /** - * Stops the timer and its task to stop updating the status of - * remote machine - */ - void stopTimerTask (); - - /** - * Sets the JSch Stack identified associated with this contact - * - * @param jsch to be associated - */ - void setJSch (JSch jsch); - - /** - * Returns the Username associated with this contact - * - * @return userName - */ - String getUserName (); - - /** - * Returns the Hostname associated with this contact - * - * @return hostName - */ - String getHostName (); - - /** - * Returns the Password associated with this contact - * - * @return password - */ - String getPassword (); - - /** - * Sets the Password associated with this contact - * - * @param password - */ - void setPassword (String password); - - /** - * Returns the SSH Session associated with this contact - * - * @return sshSession - */ - Session getSSHSession (); - - /** - * Sets the SSH Session associated with this contact - * - * @param sshSession the newly created SSH Session to be associated - */ - void setSSHSession (Session sshSession); - - /** - * Returns the SSH Shell Channel associated with this contact - * - * @return shellChannel - */ - Channel getShellChannel (); - - /** - * Sets the SSH Shell channel associated with this contact - * - * @param shellChannel to be associated with SSH Session of this contact - */ - void setShellChannel (Channel shellChannel); - - /** - * Sends a message a line to remote machine via the Shell Writer - * - * @param message to be sent - * @throws IOException if message failed to be sent - */ - public void sendLine (String message) - throws IOException; - -// /** -// * Reads a line from the remote machine via the Shell Reader -// * -// * @return message read -// */ -// public String getLine() -// throws IOException; - - /** - * Returns the Input Stream associated with SSH Channel of this contact - * - * @return shellInputStream associated with SSH Channel of this contact - */ - public InputStream getShellInputStream (); - -// /** -// * Sets the Input Stream associated with SSH Channel of this contact -// * -// * @param shellInputStream to be associated with SSH Channel of this -// * contact -// */ -// public void setShellInputStream(InputStream shellInputStream); - - /** - * Returns the Output Stream associated with SSH Channel of this contact - * - * @return shellOutputStream associated with SSH Channel of this contact - */ - public OutputStream getShellOutputStream (); - -// /** -// * Sets the Output Stream associated with SSH Channel of this contact -// * -// * @param shellOutputStream to be associated with SSH Channel of this -// * contact -// */ -// public void setShellOutputStream(OutputStream shellOutputStream); -// - /** - * Returns the BufferedReader associated with SSH Channel of this contact - * - * @return shellReader associated with SSH Channel of this contact - */ - public InputStreamReader getShellReader (); -// -// /** -// * Sets the BufferedReader associated with SSH Channel of this contact -// * -// * @param shellReader to be associated with SSH Channel of this contact -// */ -// public void setShellReader(BufferedReader shellReader); - - /** - * Returns the PrintWriter associated with SSH Channel of this contact - * - * @return shellWriter associated with SSH Channel of this contact - */ - public PrintWriter getShellWriter (); - -// /** -// * Sets the PrintWriter associated with SSH Channel of this contact -// * -// * @param shellWriter to be associated with SSH Channel of this contact -// */ -// public void setShellWriter(PrintWriter shellWriter); -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSHImpl.java deleted file mode 100644 index 15da209..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSHImpl.java +++ /dev/null @@ -1,918 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.Base64; // disambiguation -import net.java.sip.communicator.util.Logger; - -import com.jcraft.jsch.*; -// disambiguation - -/** - * A Contact of SSH Type - * - * @author Shobhit Jindal - */ -public class ContactSSHImpl - extends AbstractContact - implements ContactSSH -{ - private static final Logger logger - = Logger.getLogger(ContactSSHImpl.class); - - /** - * This acts as a separator between details stored in persistent data - */ - private final String separator = - Resources.getString("impl.protocol.ssh.DETAILS_SEPARATOR"); - - /** - * The identifier for SSH Stack - * Java Secure Channel JSch - */ - private JSch jsch; - - /** - * Interface for user to provide details about machine - */ - private SSHContactInfo sshConfigurationForm; - - /** - * A Timer Daemon to update the status of this contact - */ - private Timer timer = new Timer(true); - - /** - * A Daemon to retrieve and fire messages received from remote machine - */ - private SSHReaderDaemon contactSSHReaderDaemon; - - /** - * The id of the contact. - */ - private String contactID = null; - - /** - * The persistentData of the contact. - */ - private String persistentData = null; - -// /** -// * This stores the prompt string of shell -// */ -// private String sshPrompt; - - /** - * The provider that created us. - */ - private ProtocolProviderServiceSSHImpl parentProvider = null; - - /** - * The identifier of the type of message received from server - */ - private int messageType; - - - - /** - * The identifier for SSH Session with the remote server - */ - private Session sshSession = null; - - /** - * The identifier for a sshShellChannel with the remote server is of type - * shell - for an interactive SSH Session with the remote machine - * - * Other types - * sftp - to tranfer files from/to the remote machine - * exec - X forwarding - * direct-tcpip - stream forwarding - */ - private Channel sshShellChannel = null; - - /** - * The identifier for the Shell Input Stream associated with SSH Sesion - */ - private InputStream shellInputStream = null; - - /** - * The identifier for the Shell Output Stream associated with SSH Sesion - */ - private OutputStream shellOutputStream = null; - - /** - * Higher wrapper for shellInputStream - */ - private InputStreamReader shellReader = null; - - /** - * Higher wrapper for shellOutputStream - */ - private PrintWriter shellWriter = null; - - /** - * The group that belong to. - */ - private ContactGroupSSHImpl parentGroup = null; - - /** - * The presence status of the contact. - */ - private PresenceStatus presenceStatus = SSHStatusEnum.NOT_AVAILABLE; - - /** - * Determines whether this contact is persistent, i.e. member of the contact - * list or whether it is here only temporarily. - */ - private boolean isPersistent = false; - - /** - * 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; - - /** - * Determines whether an connection attempt to remote server is already - * underway - */ - private boolean isConnectionInProgress = false; - - /** - * Determines whether the message received from remote machine is as a - * result of command sent to it - */ - private boolean commandSent = false; - - /** - * A lock to synchronize the access of commandSent boolean object - * with the reader thread. - */ - private final Object lock = new Object(); - - /** - * 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 ContactSSHImpl( - String id, - ProtocolProviderServiceSSHImpl parentProvider) - { - this.contactID = id; - this.parentProvider = parentProvider; - - this.sshConfigurationForm = - new SSHContactInfo(this); - - this.savePersistentDetails(); - } - - /** - * Initializes the reader and writers associated with shell of this contact - * - * @param shellInputStream The InputStream of stack - * @param shellOutputStream The OutputStream of stack - */ - public void initializeShellIO( - InputStream shellInputStream, - OutputStream shellOutputStream) - { - this.shellInputStream = shellInputStream; - this.shellOutputStream = shellOutputStream; - shellReader = new InputStreamReader(shellInputStream); - shellWriter = new PrintWriter(shellOutputStream); - - contactSSHReaderDaemon = new SSHReaderDaemon(this); - contactSSHReaderDaemon.setDaemon(true); - contactSSHReaderDaemon.isActive(true); - contactSSHReaderDaemon.start(); - } - - /** - * Closes the readers and writer associated with shell of this contact - */ - public void closeShellIO() - { - try - { - shellReader.close(); - shellInputStream.close(); - } - catch(IOException ex) - {} - - try - { - shellWriter.close(); - shellOutputStream.close(); - } - catch(IOException ex) - {} - - shellInputStream = null; - - shellReader = null; - - shellOutputStream = null; - - shellWriter = null; - - try - { - sshShellChannel.disconnect(); - } - catch(Exception e) - {} - - // Removing the reference to current channel - // a new shell channel will be created for the next message - sshShellChannel = null; - - // remove the reference of session if it were also disconnected - // like in the case of exit command - if(!sshSession.isConnected()) - { - sshSession = null; - jsch = null; - } - - ((OperationSetPersistentPresenceSSHImpl) - getParentPresenceOperationSet()). - changeContactPresenceStatus(this, SSHStatusEnum.ONLINE); - } - - /** - * Sends a message a line to remote machine via the Shell Writer - * - * @param message to be sent - */ - public void sendLine(String message) - throws IOException - { -// logger.debug("SSH TO: " + this.contactID + ": " + message); - shellWriter.println(message); - shellWriter.flush(); - } - - /** - * Reads a line from the remote machine via the Shell Reader - * - * @return message read - */ -// public String getLine() -// throws IOException -// { -// String line = shellReader.readLine(); -//// logger.debug("SSH FROM: " + this.contactID + ": " + line); -// -// // null is never returned normally, the reading attempt returs a -// // string -// // or blocks until one line is available -// if(line == null) -// { -// sshShellChannel.disconnect(); -// sshShellChannel = null; -// sshSession = null; -// throw(new IOException("Unexpected Reply from remote Server")); -// } -// return line; -// } - - /** - * Starts the timer and its task to periodically update the status of - * remote machine - */ - public void startTimerTask() - { - timer.scheduleAtFixedRate(new ContactTimerSSHImpl(this), - 2000, sshConfigurationForm.getUpdateInterval()*1000); - } - - /** - * Stops the timer and its task to stop updating the status of - * remote machine - */ - public void stopTimerTask() - { - timer.cancel(); - } - - - /** - * Saves the details of contact in persistentData seperated by - * separator - * Passowrd is saved unsecurely using Base64 encoding - */ - public void savePersistentDetails() - { - persistentData = - this.sshConfigurationForm.getHostName() + - separator + - this.sshConfigurationForm.getUserName() + - separator + - new String(Base64.encode(this.sshConfigurationForm.getPassword() - .getBytes())) + - separator + sshConfigurationForm.getPort() + - separator + - sshConfigurationForm.getTerminalType() + - separator + - sshConfigurationForm.getUpdateInterval(); - } - - /** - * Stores persistent data in fields of the contact seperated by - * separator. - * - * @param persistentData of the contact - */ - public void setPersistentData(String persistentData) - { - try - { - this.persistentData = persistentData; - int firstCommaIndex = this.persistentData.indexOf(separator); - int secondCommaIndex = this.persistentData.indexOf(separator, - firstCommaIndex +1); - int thirdCommaIndex = this.persistentData.indexOf(separator, - secondCommaIndex +1); - int fourthCommaIndex = this.persistentData.indexOf(separator, - thirdCommaIndex +1); - int fifthCommaIndex = this.persistentData.indexOf(separator, - fourthCommaIndex +1); - - if (logger.isDebugEnabled()) - logger.debug("Commas: " + firstCommaIndex + " " + secondCommaIndex + " " - + thirdCommaIndex + " " +fourthCommaIndex + " " - +fifthCommaIndex); - - this.sshConfigurationForm.setHostNameField( - this.persistentData.substring(0,firstCommaIndex)); - - this.sshConfigurationForm.setUserNameField( - this.persistentData.substring(firstCommaIndex+1, - secondCommaIndex)); - - if( (thirdCommaIndex - secondCommaIndex) > 1) - { - if(this.persistentData.substring(secondCommaIndex+1).length()>0) - this.sshConfigurationForm.setPasswordField( - new String(Base64.decode(this.persistentData - .substring(secondCommaIndex+1, thirdCommaIndex)))); - } - - - this.sshConfigurationForm.setPort( - this.persistentData.substring(thirdCommaIndex + 1, - fourthCommaIndex)); - - this.sshConfigurationForm.setTerminalType( - this.persistentData.substring(fourthCommaIndex + 1, - fifthCommaIndex)); - - this.sshConfigurationForm.setUpdateInterval( - Integer.parseInt(this.persistentData.substring(fifthCommaIndex+1))); - } - catch(Exception ex) - { - logger.error("Error setting persistent data!", ex); - } - } - - /** - * Determines whether a connection to a remote server is already underway - * - * @return isConnectionInProgress - */ - public boolean isConnectionInProgress() - { - return this.isConnectionInProgress; - } - - /** - * Sets the status of connection attempt to remote server - * This method is synchronized - * - * @param isConnectionInProgress - */ - public synchronized void setConnectionInProgress( - boolean isConnectionInProgress) - { - this.isConnectionInProgress = isConnectionInProgress; - } - - /** - * Returns the SSHContactInfo associated with this contact - * - * @return sshConfigurationForm - */ - public SSHContactInfo getSSHConfigurationForm() - { - return this.sshConfigurationForm; - } - - /** - * Returns the JSch Stack identified associated with this contact - * - * @return jsch - */ - public JSch getJSch() - { - return this.jsch; - } - - /** - * Sets the JSch Stack identified associated with this contact - * - * @param jsch to be associated - */ - public void setJSch(JSch jsch) - { - this.jsch = jsch; - } - - /** - * This method is only called when the contact is added to a new - * <tt>ContactGroupSSHImpl</tt> by the - * <tt>ContactGroupSSHImpl</tt> itself. - * - * @param newParentGroup the <tt>ContactGroupSSHImpl</tt> that is now - * parent of this <tt>ContactSSHImpl</tt> - */ - public void setParentGroup(ContactGroupSSHImpl newParentGroup) - { - this.parentGroup = newParentGroup; - } - - /** - * Returns the Hostname associated with this contact - * - * @return hostName - */ - public String getHostName() - { - return sshConfigurationForm.getHostName(); - } - - /** - * 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 true if a command has been sent whos reply was not received yet - * false otherwise - * - * @return commandSent - */ - public boolean isCommandSent() - { - return this.commandSent; - } - - /** - * Set the state of commandSent variable which determines whether a reply - * to a command sent is awaited - */ - public void setCommandSent(boolean commandSent) - { - synchronized(lock) - { - this.commandSent = commandSent; - } - } - - /** - * Return the type of message received from remote server - * - * @return messageType - */ - public int getMessageType() - { - return this.messageType; - } - - /** - * Sets the type of message received from remote server - * - * @param messageType - */ - public void setMessageType(int messageType) - { - this.messageType = messageType; - } - - /** - * Returns the status of the contact. - * - * @return presenceStatus - */ - public PresenceStatus getPresenceStatus() - { - return this.presenceStatus; - } - - /** - * Sets <tt>sshPresenceStatus</tt> as the PresenceStatus that this - * contact is currently in. - * @param sshPresenceStatus the <tt>SSHPresenceStatus</tt> - * currently valid for this contact. - */ - public void setPresenceStatus(PresenceStatus sshPresenceStatus) - { - this.presenceStatus = sshPresenceStatus; - } - - /** - * Returns a reference to the protocol provider that created the contact. - * - * @return a refererence to an instance of the ProtocolProviderService - */ - public ProtocolProviderService getProtocolProvider() - { - return parentProvider; - } - - /** - * Determines whether or not this contact represents our own identity. - * - * @return true - */ - public boolean isLocal() - { - return true; - } - - /** - * Returns the group that contains this contact. - * @return a reference to the <tt>ContactGroupSSHImpl</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. - */ - @Override - public String toString() - { - StringBuffer buff - = new StringBuffer("ContactSSHImpl[ 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 persistent data of the contact. - * - * @return persistentData of the contact - */ - public String getPersistentData() - { - return persistentData; - } - - /** - * Determines whether or not this contact has been resolved against the - * server. Unresolved contacts are used when initially loading a contact - * list that has been stored in a local file until the presence operation - * set has managed to retrieve all the contact list from the server and has - * properly mapped contacts to their on-line buddies. - * - * @return true if the contact has been resolved (mapped against a buddy) - * and false otherwise. - */ - public boolean isResolved() - { - return isResolved; - } - - /** - * Makes the contact resolved or unresolved. - * - * @param resolved true to make the contact resolved; false to - * make it unresolved - */ - public void setResolved(boolean resolved) - { - this.isResolved = resolved; - } - - /** - * Returns the persistent presence operation set that this contact belongs - * to. - * - * @return the <tt>OperationSetPersistentPresenceSSHImpl</tt> that - * this contact belongs to. - */ - public OperationSetPersistentPresence - getParentPresenceOperationSet() - { - return - parentProvider - .getOperationSet(OperationSetPersistentPresence.class); - } - - /** - * Returns the BasicInstant Messaging operation set that this contact - * belongs to. - * - * @return the <tt>OperationSetBasicInstantMessagingSSHImpl</tt> that - * this contact belongs to. - */ - public OperationSetBasicInstantMessaging - getParentBasicInstantMessagingOperationSet() - { - return - parentProvider - .getOperationSet(OperationSetBasicInstantMessaging.class); - } - - /** - * Returns the File Transfer operation set that this contact belongs - * to. - * - * @return the <tt>OperationSetFileTransferSSHImpl</tt> that - * this contact belongs to. - */ - public OperationSetFileTransfer - getFileTransferOperationSet() - { - return parentProvider.getOperationSet(OperationSetFileTransfer.class); - } - - - /** - * Returns the SSH Session associated with this contact - * - * @return sshSession - */ - public Session getSSHSession() - { - return this.sshSession; - } - - /** - * Sets the SSH Session associated with this contact - * - * @param sshSession the newly created SSH Session to be associated - */ - public void setSSHSession(Session sshSession) - { - this.sshSession = sshSession; - } - - /** - * Returns the SSH Shell Channel associated with this contact - * - * @return sshShellChannel - */ - public Channel getShellChannel() - { - return this.sshShellChannel; - } - - /** - * Sets the SSH Shell channel associated with this contact - * - * @param sshShellChannel to be associated with SSH Session of this contact - */ - public void setShellChannel(Channel sshShellChannel) - { - this.sshShellChannel = sshShellChannel; - } - - /** - * Returns the Input Stream associated with SSH Channel of this contact - * - * @return shellInputStream associated with SSH Channel of this contact - */ - public InputStream getShellInputStream() - { - return this.shellInputStream; - } - -// /** -// * Sets the Input Stream associated with SSH Channel of this contact -// * -// * @param shellInputStream to be associated with SSH Channel of -// * this contact -// */ -// public void setShellInputStream(InputStream shellInputStream) -// { -// this.shellInputStream = shellInputStream; -// } - - /** - * Returns the Output Stream associated with SSH Channel of this contact - * - * @return shellOutputStream associated with SSH Channel of this contact - */ - public OutputStream getShellOutputStream() - { - return this.shellOutputStream; - } - -// /** -// * Sets the Output Stream associated with SSH Channel of this contact -// * -// * @param shellOutputStream to be associated with SSH Channel of this contact -// */ -// public void setShellOutputStream(OutputStream shellOutputStream) -// { -// this.shellOutputStream = shellOutputStream; -// } - - /** - * Returns the BufferedReader associated with SSH Channel of this contact - * - * @return shellReader associated with SSH Channel of this contact - */ - public InputStreamReader getShellReader() - { - return this.shellReader; - } - -// /** -// * Sets the BufferedReader associated with SSH Channel of this contact -// * -// * @param shellReader to be associated with SSH Channel of this contact -// */ -// public void setShellReader(BufferedReader shellReader) -// { -// this.shellReader = shellReader; -// } - - /** - * Returns the PrintWriter associated with SSH Channel of this contact - * - * @return shellWriter associated with SSH Channel of this contact - */ - public PrintWriter getShellWriter() - { - return this.shellWriter; - } - -// /** -// * Sets the PrintWriter associated with SSH Channel of this contact -// * -// * @param shellWriter to be associated with SSH Channel of this contact -// */ -// public void setShellWriter(PrintWriter shellWriter) -// { -// this.shellWriter = shellWriter; -// } - - /** - * Returns the userName associated with SSH Channel of this contact - * - * @return userName associated with SSH Channel of this contact - */ - public String getUserName() - { - return sshConfigurationForm.getUserName(); - } - - /** - * Returns the password associated with SSH Channel of this contact - * - * @return password associated with SSH Channel of this contact - */ - public String getPassword() - { - return sshConfigurationForm.getPassword(); - } - - /** - * Sets the Password associated with this contact - * - * @param password - */ - public void setPassword(String password) - { - this.sshConfigurationForm.setPasswordField(password); - savePersistentDetails(); - } - -// /** -// * Sets the PS1 prompt of the current shell of Contact -// * -// * @param sshPrompt to be associated -// */ -// public void setShellPrompt(String sshPrompt) -// { -// this.sshPrompt = sshPrompt; -// } -// -// /** -// * Returns the PS1 prompt of the current shell of Contact -// * -// * @return sshPrompt -// */ -// public String getShellPrompt() -// { -// return this.sshPrompt; -// } - - /** - * Return the current status message of this contact. - * - * @return the current status message - */ - public String getStatusMessage() - { - return presenceStatus.getStatusName(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ContactTimerSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ContactTimerSSHImpl.java deleted file mode 100644 index 4e3664d..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/ContactTimerSSHImpl.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; -import java.net.*; -import java.util.*; - -import net.java.sip.communicator.util.*; - -/** - * Timer Task to update the reachability status of SSH Contact in contact list. - * (Reachability of remote machine from user's machine) - * The timer is started at either of the two places - * - A new contact - OperationSetPersistentPresenceSSHImpl - * .createUnresolvedContact - * - Existing Contact - OperationSetPersistentPresenceSSHImpl.subscribe - * - * @author Shobhit Jindal - */ -public class ContactTimerSSHImpl - extends TimerTask -{ - private static final Logger logger - = Logger.getLogger(OperationSetFileTransferSSHImpl.class); - - /** - * The contact ID of the remote machine - */ - private ContactSSH sshContact; - - /** - * PersistentPresence Identifer assoiciated with SSH Contact - */ - private OperationSetPersistentPresenceSSHImpl persistentPresence; - - /** - * The method which is called at regular intervals to update the status - * of remote machines - * - * Presently only ONLINE and OFFILINE status are checked - */ - @Override - public void run() - { - try - { - InetAddress remoteMachine = InetAddress.getByName( - sshContact.getSSHConfigurationForm().getHostName()); - - //check if machine is reachable - if(remoteMachine.isReachable( - sshContact.getSSHConfigurationForm().getUpdateInterval())) - { - if (sshContact.getPresenceStatus().equals(SSHStatusEnum.OFFLINE) - || sshContact.getPresenceStatus().equals(SSHStatusEnum - .NOT_AVAILABLE)) - { - // change status to online - persistentPresence.changeContactPresenceStatus( - sshContact, SSHStatusEnum.ONLINE); - - if (logger.isDebugEnabled()) - logger.debug("SSH Host " + sshContact - .getSSHConfigurationForm().getHostName() + ": Online"); - } - - } - else throw new IOException(); - - } - catch (IOException ex) - { - if (sshContact.getPresenceStatus().equals(SSHStatusEnum.ONLINE) - || sshContact.getPresenceStatus().equals( - SSHStatusEnum.NOT_AVAILABLE)) - { - persistentPresence.changeContactPresenceStatus( - sshContact, SSHStatusEnum.OFFLINE); - - if (logger.isDebugEnabled()) - logger.debug("SSH Host " + sshContact.getSSHConfigurationForm() - .getHostName() + ": Offline"); - } - } - } - /** - * Creates a new instance of ContactTimerSSHImpl - * - * @param sshContact the <tt>Contact</tt> - */ - public ContactTimerSSHImpl(ContactSSH sshContact) - { - super(); - this.sshContact = sshContact; - this.persistentPresence = (OperationSetPersistentPresenceSSHImpl) - sshContact.getParentPresenceOperationSet(); - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/FileTransferSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/FileTransferSSHImpl.java deleted file mode 100644 index 1e95032..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/FileTransferSSHImpl.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * SSH implementation of the <tt>AbstractFileTransfer</tt>. - * - * @author Yana Stamcheva - */ -public class FileTransferSSHImpl - extends AbstractFileTransfer -{ - private final SSHFileTransferDaemon fileTransfer; - - private final Date initialDate; - - /** - * Creates an SSH implementation of the file transfer interface. - * - * @param fileTransfer the SSH file transfer - * @param date the initial date of the transfer - */ - public FileTransferSSHImpl( SSHFileTransferDaemon fileTransfer, - Date date) - { - this.fileTransfer = fileTransfer; - this.initialDate = date; - } - - /** - * Cancels this file transfer. When this method is called transfer should - * be interrupted. - */ - @Override - public void cancel() - { - // TODO: Implement cancel() for SSH file transfer. - } - - /** - * Returns the number of bytes already transfered through this file transfer. - * - * @return the number of bytes already transfered through this file transfer - */ - @Override - public long getTransferedBytes() - { - // TODO: Implement getTransferedBytes() for SSH file transfer. - return 0; - } - - public int getDirection() - { - return IN; - } - - public File getLocalFile() - { - return null; - } - - public Contact getContact() - { - return null; - } - - public String getID() - { - return null; - } - - public Date getInitialDate() - { - return initialDate; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/MessageSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/MessageSSHImpl.java deleted file mode 100644 index a8324f7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/MessageSSHImpl.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import net.java.sip.communicator.service.protocol.*; - -/** - * Very simple message implementation for the SSH protocol. - * - * @author Shobhit Jindal - * @author Lubomir Marinov - */ -public class MessageSSHImpl - extends AbstractMessage -{ - - /** - * The content type of the message. - */ - public static String contentType = "text/plain"; - - /** - * 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 MessageSSHImpl(String content, String contentType, - String contentEncoding, String subject) - { - super(content, null, contentEncoding, subject); - - MessageSSHImpl.contentType = contentType; - } - - /** - * Returns the type of the content of this message. - * - * @return the type of the content of this message. - */ - @Override - public String getContentType() - { - return contentType; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java deleted file mode 100644 index 9073ad5..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; - -/** - * Instant messaging functionality for the SSH protocol. - * - * @author Shobhit Jindal - */ -public class OperationSetBasicInstantMessagingSSHImpl - extends AbstractOperationSetBasicInstantMessaging -{ - - /** - * The currently valid persistent presence operation set.. - */ - private OperationSetPersistentPresenceSSHImpl opSetPersPresence = null; - - /** - * The currently valid file transfer operation set - */ - private OperationSetFileTransferSSHImpl fileTransfer; - - /** - * The protocol provider that created us. - */ - private ProtocolProviderServiceSSHImpl 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 OperationSetBasicInstantMessagingSSHImpl( - ProtocolProviderServiceSSHImpl provider) - { - this.parentProvider = provider; - - this.opSetPersPresence - = (OperationSetPersistentPresenceSSHImpl) - provider - .getOperationSet(OperationSetPersistentPresence.class); - } - - @Override - public Message createMessage(String content, String contentType, - String encoding, String subject) - { - return new MessageSSHImpl(content, contentType, encoding, subject); - } - - /** - * Sends the <tt>message</tt> to the destination indicated by the - * <tt>to</tt> contact. An attempt is made to re-establish the shell - * connection if the current one is invalid. - * The reply from server is sent by a seperate reader thread - * - * @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 ContactSSHImpl) ) - throw new IllegalArgumentException( - "The specified contact is not a SSH contact." - + to); - - ContactSSH sshContact = (ContactSSH)to; - - // making sure no messages are sent and no new threads are triggered, - // until a thread trying to connect to remote server returns - if(sshContact.isConnectionInProgress()) - { - deliverMessage( - createMessage("A connection attempt is in progress"), - (ContactSSHImpl)to); - return; - } - - if( !parentProvider.isShellConnected(sshContact) ) - { - - try - { - /** - * creating a new SSH session / shell channel - * - first message - * - session is timed out - * - network problems - */ - parentProvider.connectShell(sshContact, message); - - //the first message is ignored - return; - } - catch (Exception ex) - { - throw new IllegalStateException(ex.getMessage()); - } - } - - if(wrappedMessage(message.getContent(), sshContact)) - { - fireMessageDelivered(message, to); - return; - } - - try - { - sshContact.sendLine(message.getContent()); - sshContact.setCommandSent(true); - } - catch (IOException ex) - { - // Closing IO Streams - sshContact.closeShellIO(); - - throw new IllegalStateException(ex.getMessage()); - } - - fireMessageDelivered(message, to); - } - - /** - * Check the message for wrapped Commands - * All commands begin with / - * - * @param message from user - * @param sshContact of the remote machine - * - * @return true if the message had commands, false otherwise - */ - private boolean wrappedMessage( - String message, - ContactSSH sshContact) - { - if(message.startsWith("/upload")) - { - int firstSpace = message.indexOf(' '); - - try - { - sshContact.getFileTransferOperationSet().sendFile( - sshContact, - null, - message.substring(message.indexOf(' ', firstSpace+1) + 1), - message.substring( - firstSpace+1, - message.indexOf(' ', firstSpace+1))); - } - catch (Exception e) - { - e.printStackTrace(); - } - - return true; - } - else if(message.startsWith("/download")) - { - int firstSpace = message.indexOf(' '); - - try - { - sshContact.getFileTransferOperationSet().sendFile( - null, - sshContact, - message.substring(firstSpace+1, message.indexOf(' ', - firstSpace+1)), - message.substring(message.indexOf(' ', firstSpace+1) + 1)); - } - catch(Exception e) - { - e.printStackTrace(); - } - return true; - } - return false; - } - - /** - * In case the <tt>to</tt> Contact corresponds to another ssh - * protocol provider registered with SIP Communicator, we deliver - * the message to them, in case the <tt>to</tt> Contact represents us, we - * fire a <tt>MessageReceivedEvent</tt>, and if <tt>to</tt> is simply - * a contact in our contact list, then we simply echo the message. - * - * @param message the <tt>Message</tt> the message to deliver. - * @param to the <tt>Contact</tt> that we should deliver the message to. - */ - void deliverMessage( - Message message, - ContactSSH to) - { - String userID = to.getAddress(); - - //if the user id is owr own id, then this message is being routed to us - //from another instance of the ssh provider. - if (userID.equals(this.parentProvider.getAccountID().getUserID())) - { - //check who is the provider sending the message - String sourceUserID - = to.getProtocolProvider().getAccountID().getUserID(); - - //check whether they are in our contact list - Contact from = opSetPersPresence.findContactByID(sourceUserID); - - //and if not - add them there as volatile. - if(from == null) - { - from = opSetPersPresence.createVolatileContact(sourceUserID); - } - - //and now fire the message received event. - fireMessageReceived(message, from); - } - else - { - //if userID is not our own, try an check whether another provider - //has that id and if yes - deliver the message to them. - ProtocolProviderServiceSSHImpl sshProvider - = this.opSetPersPresence.findProviderForSSHUserID(userID); - if(sshProvider != null) - { - OperationSetBasicInstantMessagingSSHImpl opSetIM - = (OperationSetBasicInstantMessagingSSHImpl) - sshProvider - .getOperationSet( - OperationSetBasicInstantMessaging.class); - opSetIM.deliverMessage(message, to); - } - else - { - //if we got here then "to" is simply someone in our contact - //list so let's just echo the message. - fireMessageReceived(message, to); - } - } - } - - /** - * Notifies all registered message listeners that a message has been - * received. - * - * @param message the <tt>Message</tt> that has been received. - * @param from the <tt>Contact</tt> that <tt>message</tt> was received from. - */ - @Override - protected void fireMessageReceived(Message message, Contact from) - { - fireMessageEvent( - new MessageReceivedEvent( - message, - from, - new Date(), - ((ContactSSH) from).getMessageType())); - } - - /** - * Determines whether the SSH protocol provider supports - * sending and receiving offline messages. - * - * @return <tt>false</tt> - */ - public boolean isOfflineMessagingSupported() - { - return false; - } - - /** - * Determines wheter 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) - { - return MessageSSHImpl.contentType.equals(contentType); - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetFileTransferSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetFileTransferSSHImpl.java deleted file mode 100644 index 61d8160..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetFileTransferSSHImpl.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; - -/** - * This class provides operations to upload/download files to remote machines - * - * @author Shobhit Jindal - */ -public class OperationSetFileTransferSSHImpl - implements OperationSetFileTransfer -{ - private static final Logger logger - = Logger.getLogger(OperationSetFileTransferSSHImpl.class); - - /** - * Currently registered message listeners. - */ - private Vector<FileTransferListener> fileTransferListeners - = new Vector<FileTransferListener>(); - - /** - * The protocol provider that created us. - */ - private ProtocolProviderServiceSSHImpl parentProvider = null; - - - /** - * Creates a new instance of OperationSetFileTransferSSHImpl - * - * @param parentProvider the parent protocol provider service - */ - public OperationSetFileTransferSSHImpl( - ProtocolProviderServiceSSHImpl parentProvider) - { - this.parentProvider = parentProvider; - } - - /** - * Registers a FileTransferListener with this operation set so that it gets - * notifications of start, complete, failure of file transfers - * - * @param listener the <tt>FileListener</tt> to register. - */ - public void addFileTransferListener( - FileTransferListener listener) - { - synchronized (fileTransferListeners) - { - if(!fileTransferListeners.contains(listener)) - fileTransferListeners.add(listener); - } - } - - public void removeFileTransferListener( - FileTransferListener listener) - { - synchronized (fileTransferListeners) - { - fileTransferListeners.remove(listener); - } - } - - /** - * Sends a file transfer request to the given <tt>toContact</tt>. - * @param toContact the contact that should receive the file - * @param file the file to send - */ - public FileTransfer sendFile( Contact toContact, - File file) - { - return this.sendFile( toContact, - null, - file.getAbsolutePath(), - file.getAbsolutePath()); - } - - /** - * The file transfer method to/from the remote machine - * either toContact is null(we are downloading file from remote machine - * or fromContact is null(we are uploading file to remote machine - * - * @param toContact - the file recipient - * @param fromContact - the file sender - * @param remotePath - the identifier for the remote file - * @param localPath - the identifier for the local file - */ - public FileTransfer sendFile( - Contact toContact, - Contact fromContact, - String remotePath, - String localPath) - { - if(toContact == null) - { - SSHFileTransferDaemon fileTransferDaemon - = new SSHFileTransferDaemon( - (ContactSSH)fromContact, - parentProvider); - - if(localPath.endsWith(System.getProperty("file.separator"))) - localPath += remotePath.substring(remotePath.lastIndexOf( - System.getProperty("file.separator")) + 1); - - fileTransferDaemon.downloadFile( - remotePath, - localPath); - - return new FileTransferSSHImpl(fileTransferDaemon, new Date()); - } - else if(fromContact == null) - { - SSHFileTransferDaemon fileTransferDaemon - = new SSHFileTransferDaemon( - (ContactSSH) toContact, - parentProvider); - - fileTransferDaemon.uploadFile( - remotePath, - localPath); - - return new FileTransferSSHImpl(fileTransferDaemon, new Date()); - } - - // code should not reach here - // assert false; - logger.error("we should not be here !"); - return null; - } - - /** - * Returns the maximum file length supported by the protocol in bytes. - * @return the file length that is supported. - */ - public long getMaximumFileLength() - { - return 2048*1024*1024; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetPersistentPresenceSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetPersistentPresenceSSHImpl.java deleted file mode 100644 index d4c63bd..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetPersistentPresenceSSHImpl.java +++ /dev/null @@ -1,980 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -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 SSH 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 Shobhit Jindal - */ -public class OperationSetPersistentPresenceSSHImpl - extends AbstractOperationSetPersistentPresence<ProtocolProviderServiceSSHImpl> -{ - private static final Logger logger = - Logger.getLogger(OperationSetPersistentPresenceSSHImpl.class); - - /** - * The root of the ssh contact list. - */ - private ContactGroupSSHImpl contactListRoot = null; - - /** - * The currently active status message. - */ - private String statusMessage = "Online"; - - /** - * Our default presence status. - */ - private PresenceStatus presenceStatus = SSHStatusEnum.ONLINE; - - /** - * Creates an instance of this operation set keeping a reference to the - * specified parent <tt>provider</tt>. - * @param provider the ProtocolProviderServiceSSHImpl instance that - * created us. - */ - public OperationSetPersistentPresenceSSHImpl( - ProtocolProviderServiceSSHImpl provider) - { - super(provider); - - contactListRoot = new ContactGroupSSHImpl("RootGroup", provider); - - //add our unregistration listener - parentProvider.addRegistrationStateChangeListener( - new UnregistrationListener()); - } - - /** - * This function changes the status of contact as well as that of the - * provider - * - * @param sshContact the contact of the remote machine - * @param newStatus new status of the contact - */ - public void changeContactPresenceStatus( - ContactSSH sshContact, - PresenceStatus newStatus) - { - PresenceStatus oldStatus = sshContact.getPresenceStatus(); - sshContact.setPresenceStatus(newStatus); - fireContactPresenceStatusChangeEvent( - sshContact - , sshContact.getParentContactGroup() - , oldStatus); - fireProviderStatusChangeEvent(oldStatus); - } - - /** - * 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) - { - ContactGroupSSHImpl newGroup - = new ContactGroupSSHImpl(groupName, parentProvider); - - ((ContactGroupSSHImpl)parent).addSubgroup(newGroup); - - this.fireServerStoredGroupEvent( - newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT); - } - - /** - * A SSH Provider method to use for fast filling of a contact list. - * - * @param contactGroup the group to add - */ - public void addSSHGroup(ContactGroupSSHImpl contactGroup) - { - contactListRoot.addSubgroup(contactGroup); - } - - /** - * A SSH 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 addSSHGroupAndFireEvent( - ContactGroupSSHImpl parent, - ContactGroupSSHImpl 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<PresenceStatus> getSupportedStatusSet() - { - return SSHStatusEnum.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) - { - ContactSSHImpl sshContact - = (ContactSSHImpl)contactToMove; - - ContactGroupSSHImpl parentSSHGroup - = findContactParent(sshContact); - - parentSSHGroup.removeContact(sshContact); - - //if this is a volatile contact then we haven't really subscribed to - //them so we'd need to do so here - if(!sshContact.isPersistent()) - { - //first tell everyone that the volatile contact was removed - fireSubscriptionEvent(sshContact - , parentSSHGroup - , SubscriptionEvent.SUBSCRIPTION_REMOVED); - - try - { - //now subscribe - this.subscribe(newParent, contactToMove.getAddress()); - - //now tell everyone that we've added the contact - fireSubscriptionEvent(sshContact - , newParent - , SubscriptionEvent.SUBSCRIPTION_CREATED); - } - catch (Exception ex) - { - logger.error("Failed to move contact " - + sshContact.getAddress() - , ex); - } - } - else - { - ( (ContactGroupSSHImpl) newParent) - .addContact(sshContact); - - fireSubscriptionMovedEvent(contactToMove - , parentSSHGroup - , 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. - */ - public void publishPresenceStatus( - PresenceStatus status, - String statusMessage) - throws IllegalArgumentException, - IllegalStateException - { - 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()) -// { -// ContactSSHImpl contact -// = (ContactSSHImpl) 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>ContactSSHImpl</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( - ContactSSH 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<Contact> childContacts = parent.contacts(); - - while(childContacts.hasNext()) - { - ContactSSHImpl contact - = (ContactSSHImpl)childContacts.next(); - - if(findProviderForSSHUserID(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<ContactGroup> subgroups = parent.subgroups(); - - while(subgroups.hasNext()) - { - ContactGroup subgroup = subgroups.next(); - changePresenceStatusForAllContacts(subgroup, newStatus); - } - } - - /** - * Returns the group that is parent of the specified sshGroup or null - * if no parent was found. - * @param sshGroup the group whose parent we're looking for. - * @return the ContactGroupSSHImpl instance that sshGroup - * belongs to or null if no parent was found. - */ - public ContactGroupSSHImpl findGroupParent( - ContactGroupSSHImpl sshGroup) - { - return contactListRoot.findGroupParent(sshGroup); - } - - /** - * Returns the group that is parent of the specified sshContact or - * null if no parent was found. - * @param sshContact the contact whose parent we're looking for. - * @return the ContactGroupSSHImpl instance that sshContact - * belongs to or null if no parent was found. - */ - public ContactGroupSSHImpl findContactParent( - ContactSSH sshContact) - { - return (ContactGroupSSHImpl)sshContact - .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 - { - ContactGroupSSHImpl sshGroup - = (ContactGroupSSHImpl)group; - - ContactGroupSSHImpl parent = findGroupParent(sshGroup); - - if(parent == null) - { - throw new IllegalArgumentException( - "group " + group - + " does not seem to belong to this protocol's contact " - + "list."); - } - - parent.removeSubGroup(sshGroup); - - this.fireServerStoredGroupEvent( - sshGroup, ServerStoredGroupEvent.GROUP_REMOVED_EVENT); - } - - /** - * 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) - { - ((ContactGroupSSHImpl)group).setGroupName(newName); - - this.fireServerStoredGroupEvent( - group, ServerStoredGroupEvent - .GROUP_RENAMED_EVENT); - } - - - /** - * 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 - { - ContactSSH sshContact = new ContactSSHImpl(contactIdentifier, - parentProvider); - -/* ProtocolProviderServiceSSHImpl.getUIService().getConfigurationWindow() - .setVisible(true); -*/ - sshContact.setParentGroup((ContactGroupSSHImpl)parent); - sshContact.getSSHConfigurationForm().setVisible(true); - - - -/* Gets the domain name or IP address of the sshContact machine via the - * UI Service Interface - sshContact.setPersistentData(ProtocolProviderServiceSSHImpl - .getUIService().getPopupDialog() - .showInputPopupDialog("Enter Domain Name or IP Address of " - + sshContact.getDisplayName())); - - // contact is added to list later after the user has provided - // details in SSHConfigurationForm - - // addContactToList method is called -*/ - } - - /** - * Add a contact to the specified group - * - * @param parent the group - * @param sshContact the contact - */ - public void addContactToList( - ContactGroup parent, - ContactSSH sshContact) - { - // Adds the sshContact to the sshContact list - - ((ContactGroupSSHImpl)parent).addContact(sshContact); - - fireSubscriptionEvent(sshContact, - parent, - SubscriptionEvent.SUBSCRIPTION_CREATED); - - //notify presence listeners for the status change. - fireContactPresenceStatusChangeEvent(sshContact - , parent - , SSHStatusEnum.NOT_AVAILABLE); - - sshContact.startTimerTask(); - } - - /** - * 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 - { - ContactGroupSSHImpl parentGroup - = (ContactGroupSSHImpl)((ContactSSHImpl)contact) - .getParentContactGroup(); - - parentGroup.removeContact((ContactSSHImpl)contact); - - fireSubscriptionEvent(contact, - ((ContactSSHImpl)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) - { - ContactSSH contact = new ContactSSHImpl( - address, - parentProvider); - - contact.setPersistentData(persistentData); - contact.startTimerTask(); - - // SSH Contacts are resolved by default - contact.setResolved(true); - - ( (ContactGroupSSHImpl) parent).addContact(contact); - - fireSubscriptionEvent(contact, - parent, - SubscriptionEvent.SUBSCRIPTION_CREATED); - - //since we don't have any server, we'll simply resolve the contact - //ourselves as if we've just received an event from the server telling - //us that it has been resolved. - fireSubscriptionEvent( - contact, parent, SubscriptionEvent.SUBSCRIPTION_RESOLVED); - - return contact; - } - - /** - * Looks for a ssh protocol provider registered for a user id matching - * <tt>sshUserID</tt>. - * - * @param sshUserID the ID of the SSH user whose corresponding - * protocol provider we'd like to find. - * @return ProtocolProviderServiceSSHImpl a ssh protocol - * provider registered for a user with id <tt>sshUserID</tt> or null - * if there is no such protocol provider. - */ - public ProtocolProviderServiceSSHImpl - findProviderForSSHUserID(String sshUserID) - { - BundleContext bc = SSHActivator.getBundleContext(); - - String osgiQuery = "(&" - + "(" + ProtocolProviderFactory.PROTOCOL - + "=" + ProtocolNames.SSH + ")" - + "(" + ProtocolProviderFactory.USER_ID - + "=" + sshUserID + ")" - + ")"; - - 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 (ProtocolProviderServiceSSHImpl)bc.getService(refs[0]); - } - - return null; - } - - /** - * Looks for ssh 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<Contact> findContactsPointingToUs() - { - List<Contact> contacts = new LinkedList<Contact>(); - BundleContext bc = SSHActivator.getBundleContext(); - - String osgiQuery = - "(" + ProtocolProviderFactory.PROTOCOL - + "=SSH)"; - - 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++) - { - ProtocolProviderServiceSSHImpl gibProvider - = (ProtocolProviderServiceSSHImpl)bc.getService(refs[i]); - - OperationSetPersistentPresenceSSHImpl opSetPersPresence - = (OperationSetPersistentPresenceSSHImpl)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) - { - ContactGroupSSHImpl newGroup - = new ContactGroupSSHImpl( - ContactGroupSSHImpl.createNameFromUID(groupUID) - , parentProvider); - newGroup.setResolved(false); - - //if parent is null then we're adding under root. - if(parentGroup == null) - parentGroup = getServerStoredContactListRoot(); - - ((ContactGroupSSHImpl)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 ssh 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<ContactGroup> groupsIter - = getServerStoredContactListRoot().subgroups(); - while (groupsIter.hasNext()) - { - ContactGroup group = groupsIter.next(); - Iterator<Contact> contactsIter = group.contacts(); - - while (contactsIter.hasNext()) - { - ContactSSHImpl contact - = (ContactSSHImpl) contactsIter.next(); - - PresenceStatus oldContactStatus - = contact.getPresenceStatus(); - - if (!oldContactStatus.isOnline()) - continue; - - contact.setPresenceStatus(SSHStatusEnum.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 ContactGroupSSHImpl getNonPersistentGroup() - { - for (int i = 0 - ; i < getServerStoredContactListRoot().countSubgroups() - ; i++) - { - ContactGroupSSHImpl gr = - (ContactGroupSSHImpl)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 ContactSSHImpl createVolatileContact(String contactAddress) - { - //First create the new volatile contact; - ContactSSHImpl newVolatileContact = new ContactSSHImpl( - contactAddress, - this.parentProvider); - - newVolatileContact.setPersistent(false); - - - //Check whether a volatile group already exists and if not create - //one - ContactGroupSSHImpl theVolatileGroup = getNonPersistentGroup(); - - - //if the parent volatile group is null then we create it - if (theVolatileGroup == null) - { - theVolatileGroup = new ContactGroupSSHImpl( - SSHActivator.getResources().getI18NString( - "service.gui.NOT_IN_CONTACT_LIST_GROUP_NAME") - , parentProvider); - theVolatileGroup.setResolved(false); - theVolatileGroup.setPersistent(false); - theVolatileGroup.addContact(newVolatileContact); - - this.contactListRoot.addSubgroup(theVolatileGroup); - - fireServerStoredGroupEvent(theVolatileGroup - , ServerStoredGroupEvent.GROUP_CREATED_EVENT); - } - - //now add the volatile contact instide it - theVolatileGroup.addContact(newVolatileContact); - fireSubscriptionEvent(newVolatileContact - , theVolatileGroup - , SubscriptionEvent.SUBSCRIPTION_CREATED); - - return newVolatileContact; - } - - /** - * DUMMY METHOD - * 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) - { - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolIconSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolIconSSHImpl.java deleted file mode 100644 index 592b2b1..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolIconSSHImpl.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * Represents the SSH protocol icon. Implements the <tt>ProtocolIcon</tt> - * interface in order to provide a SSH logo image in two different sizes. - * - * @author Shobhit Jindal - */ -public class ProtocolIconSSHImpl - implements ProtocolIcon -{ - private static Logger logger - = Logger.getLogger(ProtocolIconSSHImpl.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, - getImageInBytes("service.protocol.ssh.SSH_16x16")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_32x32, - getImageInBytes("service.protocol.ssh.SSH_32x32")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_48x48, - getImageInBytes("service.protocol.ssh.SSH_48x48")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_64x64, - getImageInBytes("service.protocol.ssh.SSH_64x64")); - } - - /** - * A hash table containing the protocol icon in different sizes. - */ - private static Hashtable<String, String> iconPathsTable - = new Hashtable<String, String>(); - static { - iconPathsTable.put(ProtocolIcon.ICON_SIZE_16x16, - SSHActivator.getResources().getImagePath( - "service.protocol.ssh.SSH_16x16")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_32x32, - SSHActivator.getResources().getImagePath( - "service.protocol.ssh.SSH_32x32")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_48x48, - SSHActivator.getResources().getImagePath( - "service.protocol.ssh.SSH_48x48")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_64x64, - SSHActivator.getResources().getImagePath( - "service.protocol.ssh.SSH_64x64")); - } - - /** - * Implements the <tt>ProtocolIcon.getSupportedSizes()</tt> method. Returns - * an iterator to a set containing the supported icon sizes. - * @return an iterator to a set containing the supported icon sizes - */ - public Iterator<String> getSupportedSizes() - { - return iconsTable.keySet().iterator(); - } - - /** - * Returns TRUE if a icon with the given size is supported, FALSE-otherwise. - * - * @return TRUE if a icon with the given size is supported, FALSE otherwise - */ - public boolean isSizeSupported(String iconSize) - { - return iconsTable.containsKey(iconSize); - } - - /** - * Returns the icon image in the given size. - * @param iconSize the icon size; one of ICON_SIZE_XXX constants - * @return the icon - */ - public byte[] getIcon(String iconSize) - { - return iconsTable.get(iconSize); - } - - /** - * Returns a path to the icon with the given size. - * @param iconSize the size of the icon we're looking for - * @return the path to the icon with the given size - */ - public String getIconPath(String iconSize) - { - return iconPathsTable.get(iconSize); - } - - /** - * Returns the icon image used to represent the protocol connecting state. - * @return the icon image used to represent the protocol connecting state - */ - public byte[] getConnectingIcon() - { - return getImageInBytes("protocolIconSsh"); - } - - /** - * Returns the byte representation of the image corresponding to the given - * identifier. - * - * @param imageID the identifier of the image - * @return the byte representation of the image corresponding to the given - * identifier. - */ - public static byte[] getImageInBytes(String imageID) - { - InputStream in = SSHActivator.getResources(). - getImageInputStream(imageID); - - if (in == null) - return null; - byte[] image = null; - try - { - image = new byte[in.available()]; - - in.read(image); - } - catch (IOException e) - { - logger.error("Failed to load image:" + imageID, e); - } - - return image; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSH.java b/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSH.java deleted file mode 100644 index 8c38f60..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSH.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.java.sip.communicator.impl.protocol.ssh; - -import net.java.sip.communicator.service.protocol.*; - -import org.osgi.framework.*; - -/** - * - * @author Shobhit Jindal - */ -public abstract class ProtocolProviderFactorySSH - extends ProtocolProviderFactory -{ - /** - * The name of a property representing the IDENTITY_FILE of the protocol for - * a ProtocolProviderFactory. - */ - public static final String IDENTITY_FILE = "IDENTITY_FILE"; - - /** - * The name of a property representing the KNOWN_HOSTS_FILE of the protocol - * for a ProtocolProviderFactory. - */ - public static final String KNOWN_HOSTS_FILE = "KNOWN_HOSTS_FILE"; - - protected ProtocolProviderFactorySSH(BundleContext bundleContext, - String protocolName) - { - super(bundleContext, protocolName); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSHImpl.java deleted file mode 100644 index 68e33bc..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSHImpl.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -import org.osgi.framework.*; - -/** - * The SSH protocol provider factory creates instances of the SSH - * protocol provider service. One Service instance corresponds to one account. - * - * @author Shobhit Jindal - */ -public class ProtocolProviderFactorySSHImpl - extends ProtocolProviderFactorySSH -{ - - /** - * Creates an instance of the ProtocolProviderFactorySSHImpl. - */ - public ProtocolProviderFactorySSHImpl() - { - super(SSHActivator.getBundleContext(), ProtocolNames.SSH); - } - - /** - * 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. - */ - @Override - public AccountID installAccount( - String userIDStr, - Map<String, String> accountProperties) - { - BundleContext context = SSHActivator.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 SSHAccountID(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(accountID, false); - - accountID = loadAccount(accountProperties); - -/* ServiceReference ppServiceRef = context - .getServiceReference(ProtocolProviderService.class.getName()); - - ProtocolProviderService ppService = (ProtocolProviderService) - context.getService(ppServiceRef); - - OperationSetPersistentPresence operationSetPersistentPresence = - (OperationSetPersistentPresence) ppService.getOperationSet( - OperationSetPersistentPresence.class); - - try - { - // The below should never fail for SSH accounts - operationSetPersistentPresence.subscribe(userIDStr); - - } - catch(OperationFailedException ex) - { - ex.printStackTrace(); - } -*/ - return accountID; - } - - @Override - protected AccountID createAccountID(String userID, Map<String, String> accountProperties) - { - return new SSHAccountID(userID, accountProperties); - } - - @Override - protected ProtocolProviderService createService(String userID, - AccountID accountID) - { - ProtocolProviderServiceSSHImpl service = - new ProtocolProviderServiceSSHImpl(); - - service.initialize(userID, accountID); - return service; - } - -// /** -// * 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(SSHActivator.getBundleContext(), -// accountID, -// String.valueOf(Base64.encode(passwd.getBytes()))); -// } -// -// /** -// * 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 -// { -// String password = super.loadPassword(SSHActivator.getBundleContext() -// , accountID ); -// return(String.valueOf(Base64.decode(password))); -// } - - @Override - public void modifyAccount( ProtocolProviderService protocolProvider, - Map<String, String> accountProperties) - throws NullPointerException - { - // TODO Auto-generated method stub - - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderServiceSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderServiceSSHImpl.java deleted file mode 100644 index a4b16e6..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderServiceSSHImpl.java +++ /dev/null @@ -1,662 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; - -import javax.swing.*; - -import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.Logger; - -import org.osgi.framework.*; - -import com.jcraft.jsch.*; - -/** - * A SSH implementation of the ProtocolProviderService. - * - * @author Shobhit Jindal - */ -public class ProtocolProviderServiceSSHImpl - extends AbstractProtocolProviderService -{ - private static final Logger logger - = Logger.getLogger(ProtocolProviderServiceSSHImpl.class); - - /** - * The name of this protocol. - */ - public static final String SSH_PROTOCOL_NAME = ProtocolNames.SSH; - -// /** -// * The identifier for SSH Stack -// * Java Secure Channel JSch -// */ -// JSch jsch = new JSch(); - - /** - * The test command given after each command to determine the reply length - * of the command - */ - //private final String testCommand = - // Resources.getString("testCommand"); - - /** - * A reference to the protocol provider of UIService - */ - private static ServiceReference ppUIServiceRef; - - /** - * Connection timeout to a remote server in milliseconds - */ - private static int connectionTimeout = 30000; - - /** - * A reference to UI Service - */ - private static UIService uiService; - - /** - * The id of the account that this protocol provider represents. - */ - private AccountID accountID = null; - - /** - * We use this to lock access to initialization. - */ - private final Object initializationLock = new Object(); - - private OperationSetBasicInstantMessagingSSHImpl basicInstantMessaging; - - private OperationSetFileTransferSSHImpl fileTranfer; - - /** - * Indicates whether or not the provider is initialized and ready for use. - */ - private boolean isInitialized = false; - - /** - * The logo corresponding to the ssh protocol. - */ - private ProtocolIconSSHImpl sshIcon - = new ProtocolIconSSHImpl(); - - /** - * The registration state of SSH Provider is taken to be registered by - * default as it doesn't correspond to the state on remote server - */ - private RegistrationState currentRegistrationState - = RegistrationState.REGISTERED; - - /** - * The default constructor for the SSH protocol provider. - */ - public ProtocolProviderServiceSSHImpl() - { - if (logger.isTraceEnabled()) - logger.trace("Creating a ssh provider."); - - try - { - // converting to milliseconds - connectionTimeout = Integer.parseInt(Resources.getString( - "connectionTimeout")) * 1000; - } - catch(NumberFormatException ex) - { - logger.error("Connection Timeout set to 30 seconds"); - } - } - - /** - * 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 ssh 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 - OperationSetPersistentPresenceSSHImpl persistentPresence = - new OperationSetPersistentPresenceSSHImpl(this); - - addSupportedOperationSet( - OperationSetPersistentPresence.class, - persistentPresence); - //register it once again for those that simply need presence and - //won't be smart enough to check for a persistent presence - //alternative - addSupportedOperationSet( - OperationSetPresence.class, - persistentPresence); - - //initialize the IM operation set - basicInstantMessaging = new - OperationSetBasicInstantMessagingSSHImpl( - this); - addSupportedOperationSet( - OperationSetBasicInstantMessaging.class, - basicInstantMessaging); - - //initialze the file transfer operation set - fileTranfer = new OperationSetFileTransferSSHImpl(this); - addSupportedOperationSet( - OperationSetFileTransfer.class, - fileTranfer); - - isInitialized = true; - } - } - - /** - * Determines whether a vaild session exists for the contact of remote - * machine. - * - * @param sshContact ID of SSH Contact - * - * @return <tt>true</tt> if the session is connected - * <tt>false</tt> otherwise - */ - public boolean isSessionValid(ContactSSH sshContact) - { - Session sshSession = sshContact.getSSHSession(); - if( sshSession != null) - if(sshSession.isConnected()) - return true; - - // remove reference to an unconnected SSH Session, if any - sshContact.setSSHSession(null); - return false; - } - - /** - * Determines whether the contact is connected to shell of remote machine - * as a precheck for any further operation - * - * @param sshContact ID of SSH Contact - * - * @return <tt>true</tt> if the contact is connected - * <tt>false</tt> if the contact is not connected - */ - public boolean isShellConnected(ContactSSH sshContact) - { - // a test command may also be run here - - if(isSessionValid(sshContact)) - { - return(sshContact.getShellChannel() != null); - } - - /* - * Above should be return(sshContact.getShellChannel() != null - * && sshContact.getShellChannel().isConnected()); - * - * but incorrect reply from stack for isConnected() - */ - - return false; - } - - /** - * Creates a shell channel to the remote machine - * a new jsch session is also created if the current one is invalid - * - * @param sshContact the contact of the remote machine - * @param firstMessage the first message - */ - public void connectShell( - final ContactSSH sshContact, - final Message firstMessage) - { - sshContact.setConnectionInProgress(true); - - final Thread newConnection = new Thread((new Runnable() - { - public void run() - { - OperationSetPersistentPresenceSSHImpl persistentPresence - = (OperationSetPersistentPresenceSSHImpl)sshContact - .getParentPresenceOperationSet(); - - persistentPresence.changeContactPresenceStatus( - sshContact, - SSHStatusEnum.CONNECTING); - - try - { - if(!isSessionValid(sshContact)) - createSSHSessionAndLogin(sshContact); - - createShellChannel(sshContact); - - //initializing the reader and writers of ssh contact - - persistentPresence.changeContactPresenceStatus( - sshContact, - SSHStatusEnum.CONNECTED); - - showWelcomeMessage(sshContact); - - sshContact.setMessageType(ContactSSH - .CONVERSATION_MESSAGE_RECEIVED); - - sshContact.setConnectionInProgress(false); - - Thread.sleep(1500); - - sshContact.setCommandSent(true); - - basicInstantMessaging.sendInstantMessage( - sshContact, - firstMessage); - } - // rigorous Exception Checking in future - catch (Exception ex) - { - persistentPresence.changeContactPresenceStatus( - sshContact, - SSHStatusEnum.NOT_AVAILABLE); - - ex.printStackTrace(); - } - finally - { - sshContact.setConnectionInProgress(false); - } - } - })); - - newConnection.start(); - } - - /** - * Creates a channel for shell type in the current session - * channel types = shell, sftp, exec(X forwarding), - * direct-tcpip(stream forwarding) etc - * - * @param sshContact ID of SSH Contact - * @throws IOException if the shell channel cannot be created - */ - public void createShellChannel(ContactSSH sshContact) - throws IOException - { - try - { - Channel shellChannel = sshContact.getSSHSession() - .openChannel("shell"); - - //initalizing the reader and writers of ssh contact - sshContact.initializeShellIO(shellChannel.getInputStream(), - shellChannel.getOutputStream()); - - ((ChannelShell)shellChannel).setPtyType( - sshContact.getSSHConfigurationForm().getTerminalType()); - - //initializing the shell - shellChannel.connect(1000); - - sshContact.setShellChannel(shellChannel); - - sshContact.sendLine("export PS1="); - } - catch (JSchException ex) - { - sshContact.setSSHSession(null); - throw new IOException("Unable to create shell channel to remote" + - " server"); - } - } - - /** - * Closes the Shell channel are associated IO Streams - * - * @param sshContact ID of SSH Contact - * @throws JSchException if something went wrong in JSch - * @throws IOException if I/O exception occurred - */ - public void closeShellChannel(ContactSSH sshContact) throws - JSchException, - IOException - { - sshContact.closeShellIO(); - sshContact.getShellChannel().disconnect(); - sshContact.setShellChannel(null); - } - - /** - * Creates a SSH Session with a remote machine and tries to login - * according to the details specified by Contact - * An appropriate message is shown to the end user in case the login fails - * - * @param sshContact ID of SSH Contact - * - * @throws JSchException if a JSch is unable to create a SSH Session with - * the remote machine - * @throws InterruptedException if the thread is interrupted before session - * connected or is timed out - * @throws OperationFailedException if not of above reasons :-) - */ - public void createSSHSessionAndLogin(ContactSSH sshContact) throws - JSchException, - OperationFailedException, - InterruptedException - { - if (logger.isInfoEnabled()) - logger.info("Creating a new SSH Session to " - + sshContact.getHostName()); - - // creating a new JSch Stack identifier for contact - JSch jsch = new JSch(); - - String knownHosts = - accountID.getAccountPropertyString("KNOWN_HOSTS_FILE"); - - if(!knownHosts.equals("Optional")) - jsch.setKnownHosts(knownHosts); - - String identitiyKey = - accountID.getAccountPropertyString("IDENTITY_FILE"); - - String userName = sshContact.getUserName(); - - // use the name of system user if the contact has not supplied SSH - // details - if(userName.equals("")) - userName = System.getProperty("user.name"); - - if(!identitiyKey.equals("Optional")) - jsch.addIdentity(identitiyKey); - - // creating a new session for the contact - Session session = jsch.getSession( - userName, - sshContact.getHostName(), - sshContact.getSSHConfigurationForm().getPort()); - - /** - * Creating and associating User Info with the session - * User Info passes authentication from sshContact to SSH Stack - */ - SSHUserInfo sshUserInfo = new SSHUserInfo(sshContact); - - session.setUserInfo(sshUserInfo); - - /** - * initializing the session - */ - session.connect(connectionTimeout); - - int count = 0; - - // wait for session to get connected - while(!session.isConnected() && count<=30000) - { - Thread.sleep(1000); - count += 1000; - if (logger.isTraceEnabled()) - logger.trace("SSH:" + sshContact.getHostName() - + ": Sleep zzz .. " ); - } - - // if timeout have exceeded - if(count>30000) - { - sshContact.setSSHSession(null); - JOptionPane.showMessageDialog( - null, - "SSH Connection attempt to " - + sshContact.getHostName() + " timed out"); - - // error codes are not defined yet - throw new OperationFailedException("SSH Connection attempt to " + - sshContact.getHostName() + " timed out", 2); - } - - sshContact.setJSch(jsch); - sshContact.setSSHSession(session); - - if (logger.isInfoEnabled()) - logger.info("A new SSH Session to " + sshContact.getHostName() - + " Created"); - } - - /** - * Closes the SSH Session associated with the contact - * - * @param sshContact ID of SSH Contact - */ - void closeSSHSession(ContactSSH sshContact) - { - sshContact.getSSHSession().disconnect(); - sshContact.setSSHSession(null); - } - - /** - * Presents the login welcome message to user - * - * @param sshContact ID of SSH Contact - * @throws IOException if I/O exception occurred - */ - public void showWelcomeMessage(ContactSSH sshContact) - throws IOException - { -/* //sending the command - sshContact.sendLine(testCommand); - - String reply = "", line = ""; - - // message is extracted until the test Command ie echoed back - while(line.indexOf(testCommand) == -1) - { - reply += line + "\n"; - line = sshContact.getLine(); - } - - uiService.getPopupDialog().showMessagePopupDialog - (reply,"Message from " + sshContact.getDisplayName(), - uiService.getPopupDialog().INFORMATION_MESSAGE); - - if(line.startsWith(testCommand)) - while(!sshContact.getLine().contains(testCommand)); - - //one line output of testCommand - sshContact.getLine(); -*/ - if (logger.isDebugEnabled()) - logger.debug("SSH: Welcome message shown"); - } - - /** - * Returns a reference to UIServce for accessing UI related services - * - * @return uiService a reference to UIService - */ - public static UIService getUIService() - { - return uiService; - } - - /** - * 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 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 SSH_PROTOCOL_NAME; - } - - /** - * Returns the state of the registration of this protocol provider with - * the corresponding registration service. - * - * @return ProviderRegistrationState - */ - public RegistrationState getRegistrationState() - { - return currentRegistrationState; - } - - /** - * 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; - - //get a reference to UI Service via its Service Reference - ppUIServiceRef = SSHActivator.getBundleContext() - .getServiceReference(UIService.class.getName()); - - uiService = (UIService)SSHActivator.getBundleContext() - .getService(ppUIServiceRef); - - 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; - } - if (logger.isTraceEnabled()) - logger.trace("Killing the SSH Protocol Provider."); - - if(isRegistered()) - { - try - { - //do the unregistration - unregister(); - } - catch (OperationFailedException ex) - { - //we're shutting down so we need to silence the exception here - logger.error( - "Failed to properly unregister before shutting down. " - + getAccountID() - , ex); - } - } - - isInitialized = false; - } - - /** - * Ends the registration of this protocol provider with the current - * registration service. - * - * @throws OperationFailedException with the corresponding code it the - * registration fails for some reason (e.g. a networking error or an - * implementation problem). - */ - public void unregister() - throws OperationFailedException - { - RegistrationState oldState = currentRegistrationState; - currentRegistrationState = RegistrationState.UNREGISTERED; - - fireRegistrationStateChanged( - oldState - , currentRegistrationState - , RegistrationStateChangeEvent.REASON_USER_REQUEST - , null); - } - - /* - * (non-Javadoc) - * - * @see net.java.sip.communicator.service.protocol.ProtocolProviderService# - * isSignallingTransportSecure() - */ - public boolean isSignalingTransportSecure() - { - return false; - } - - /** - * Returns the "transport" protocol of this instance used to carry the - * control channel for the current protocol service. - * - * @return The "transport" protocol of this instance: TCP. - */ - public TransportProtocol getTransportProtocol() - { - return TransportProtocol.TCP; - } - - /** - * Returns the ssh protocol icon. - * @return the ssh protocol icon - */ - public ProtocolIcon getProtocolIcon() - { - return sshIcon; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/Resources.java b/src/net/java/sip/communicator/impl/protocol/ssh/Resources.java deleted file mode 100644 index 48b9b49..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/Resources.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import net.java.sip.communicator.service.resources.*; - -/** - * @author Shobhit Jindal - */ -public class Resources -{ - /** - * The SSH logo imageID. - */ - public static ImageID SSH_LOGO = new ImageID("protocolIconSsh"); - - /** - * Returns an string corresponding to the given key. - * - * @param key The key of the string. - * - * @return a string corresponding to the given key. - */ - public static String getString(String key) - { - return SSHActivator.getResources().getI18NString(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) - { - return SSHActivator.getResources().getImageInBytes(imageID.getId()); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHAccountID.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHAccountID.java deleted file mode 100644 index 8f9dda7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHAccountID.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.java.sip.communicator.impl.protocol.ssh; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * The SSH implementation of a sip-communicator account id. - * @author Shobhit Jindal - */ -public class SSHAccountID - extends AccountID -{ - /** - * Creates an account id from the specified id and account properties. - * - * @param userID the user identifier correspnding to thi account - * @param accountProperties any other properties necessary for the account. - */ - SSHAccountID(String userID, Map<String, String> accountProperties) - { - super(userID - , accountProperties - , "SSH" - , "sip-communicator.org"); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHActivator.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHActivator.java deleted file mode 100644 index 7108020..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHActivator.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.resources.*; -import net.java.sip.communicator.util.*; - -import org.jitsi.service.resources.*; -import org.osgi.framework.*; - -/** - * Loads the SSH provider factory and registers its services in the OSGI - * bundle context. - * - * @author Shobhit Jindal - */ -public class SSHActivator - implements BundleActivator -{ - private static final Logger logger - = Logger.getLogger(SSHActivator.class); - - /** - * A reference to the registration of our SSH protocol provider - * factory. - */ - private ServiceRegistration sshPpFactoryServReg = null; - - /** - * A reference to the SSH protocol provider factory. - */ - private static ProtocolProviderFactorySSHImpl - sshProviderFactory = null; - - /** - * The currently valid bundle context. - */ - private static BundleContext bundleContext = null; - - private static ResourceManagementService resourcesService; - - /** - * Called when this bundle is started. In here we'll export the - * ssh 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<String, String> hashtable = new Hashtable<String, String>(); - hashtable.put(ProtocolProviderFactory.PROTOCOL, "SSH"); - - sshProviderFactory = new ProtocolProviderFactorySSHImpl(); - - //reg the ssh provider factory. - sshPpFactoryServReg = context.registerService( - ProtocolProviderFactory.class.getName(), - sshProviderFactory, - hashtable); - - if (logger.isInfoEnabled()) - logger.info("SSH protocol implementation [STARTED]."); - } - - /** - * Returns a reference to the bundle context that we were started with. - * @return bundleContext a reference to the BundleContext instance - * that we were started with. - */ - public static BundleContext getBundleContext() - { - return bundleContext; - } - - /** - * Retrurns a reference to the protocol provider factory that we have - * registered. - * @return a reference to the <tt>ProtocolProviderFactoryJabberImpl</tt> - * instance that we have registered from this package. - */ - public static ProtocolProviderFactorySSHImpl - getProtocolProviderFactory() - { - return sshProviderFactory; - } - - - /** - * 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 - { - sshProviderFactory.stop(); - sshPpFactoryServReg.unregister(); - if (logger.isInfoEnabled()) - logger.info("SSH protocol implementation [STOPPED]."); - } - - /** - * Returns the <tt>ResourceManagementService</tt>. - * - * @return the <tt>ResourceManagementService</tt>. - */ - public static ResourceManagementService getResources() - { - if (resourcesService == null) - resourcesService = - ResourceManagementServiceUtils.getService(bundleContext); - return resourcesService; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHContactInfo.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHContactInfo.java deleted file mode 100644 index 47bb484..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHContactInfo.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.awt.*; -import java.awt.event.*; -import java.text.*; - -import javax.swing.*; -import javax.swing.text.*; - -import net.java.sip.communicator.plugin.desktoputil.*; - -/** - * @author Shobhit Jindal - */ -class SSHContactInfo - extends SIPCommDialog -{ - /** - * Serial version UID. - */ - private static final long serialVersionUID = 0L; - - private ContactSSH sshContact; - - private JPanel mainPanel = new TransparentPanel(); - private JPanel machinePanel = new TransparentPanel(); - private JPanel detailNamesPanel = new TransparentPanel(); - private JPanel detailFieldsPanel = new TransparentPanel(); - private JPanel detailsPanel = new TransparentPanel(); - - private JCheckBox addDetailsCheckBox = new SIPCommCheckBox("Add Details"); - - private JButton doneButton = new JButton("Done"); - private JLabel machineID = new JLabel("Hostname / IP: "); - private JTextField machineIDField = new JTextField(); - private JLabel userName = new JLabel("User Name: "); - private JTextField userNameField = new JTextField(); - private JLabel password = new JLabel("Password: "); - private JTextField passwordField = new JPasswordField(); - private JLabel port = new JLabel("Port: "); - - private JFormattedTextField portField; - private JLabel secs = new JLabel("secs"); - private JLabel statusUpdate = new JLabel("Update Interval: "); - private JLabel terminalType = new JLabel("Terminal Type: "); - private JTextField terminalTypeField = new JTextField("SIP Communicator"); - private JSpinner updateTimer = new JSpinner(); - - private JPanel emptyPanel1 = new TransparentPanel(); - - private JPanel emptyPanel2 = new TransparentPanel(); - - private JPanel emptyPanel3 = new TransparentPanel(); - - private JPanel emptyPanel4 = new TransparentPanel(); - - private JPanel emptyPanel5 = new TransparentPanel(); - - private JPanel emptyPanel6 = new TransparentPanel(); - - private JPanel emptyPanel7 = new TransparentPanel(); - - private JPanel emptyPanel8 = new TransparentPanel(); - - private JPanel emptyPanel9 = new TransparentPanel(); - - private JPanel emptyPanel10 = new TransparentPanel(); - - private JPanel emptyPanel11 = new TransparentPanel(); - -// private ContactGroup contactGroup = null; - - /** - * Creates a new instance of SSHContactInfo - * - * @param sshContact the concerned contact - */ - public SSHContactInfo(ContactSSH sshContact) { - super(true); - - this.sshContact = sshContact; - initForm(); - - this.getContentPane().add(mainPanel); - - this.setSize(370, 325); - - this.setResizable(false); - - this.setTitle("SSH: Account Details of " + sshContact.getDisplayName()); - - Toolkit toolkit = Toolkit.getDefaultToolkit(); - Dimension screenSize = toolkit.getScreenSize(); - - int x = (screenSize.width - this.getWidth()) / 2; - int y = (screenSize.height - this.getHeight()) / 2; - - this.setLocation(x,y); - -// ProtocolProviderServiceSSHImpl.getUIService().getConfigurationWindow(). -// addConfigurationForm(this); - } - - /** - * initialize the form. - */ - public void initForm() { - updateTimer.setValue(30); - MaskFormatter maskFormatter = new MaskFormatter(); - try { - maskFormatter.setMask("#####"); - } catch (ParseException ex) { - ex.printStackTrace(); - } - maskFormatter.setAllowsInvalid(false); - portField = new JFormattedTextField(maskFormatter); - portField.setValue(22); - - userNameField.setEnabled(false); - passwordField.setEditable(false); - portField.setEnabled(false); - terminalTypeField.setEnabled(false); - updateTimer.setEnabled(false); - - mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); - machinePanel.setLayout(new BoxLayout(machinePanel, BoxLayout.X_AXIS)); - detailNamesPanel.setLayout(new BoxLayout(detailNamesPanel, - BoxLayout.Y_AXIS)); - detailFieldsPanel.setLayout(new BoxLayout(detailFieldsPanel, - BoxLayout.Y_AXIS)); - detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.X_AXIS)); - - machinePanel.add(machineID); - machinePanel.add(machineIDField); - - detailNamesPanel.add(userName); - detailNamesPanel.add(emptyPanel1); - detailNamesPanel.add(password); - detailNamesPanel.add(emptyPanel2); - detailNamesPanel.add(port); - detailNamesPanel.add(emptyPanel3); - detailNamesPanel.add(statusUpdate); - detailNamesPanel.add(emptyPanel4); - detailNamesPanel.add(terminalType); - - detailFieldsPanel.add(userNameField); - detailFieldsPanel.add(emptyPanel5); - detailFieldsPanel.add(passwordField); - detailFieldsPanel.add(emptyPanel6); - detailFieldsPanel.add(portField); - detailFieldsPanel.add(emptyPanel7); - detailFieldsPanel.add(updateTimer); - detailFieldsPanel.add(emptyPanel8); - detailFieldsPanel.add(terminalTypeField); - - detailsPanel.add(detailNamesPanel); - detailsPanel.add(detailFieldsPanel); - - detailsPanel.setBorder(BorderFactory.createTitledBorder("Details")); - - mainPanel.add(emptyPanel9); - mainPanel.add(machinePanel); - mainPanel.add(addDetailsCheckBox); - mainPanel.add(detailsPanel); - mainPanel.add(emptyPanel10); - mainPanel.add(doneButton); - mainPanel.add(emptyPanel11); - - addDetailsCheckBox.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent event) { - addDetailsCheckBox.setEnabled(false); - userNameField.setEnabled(true); - passwordField.setEditable(true); - portField.setEnabled(true); - terminalTypeField.setEnabled(true); - updateTimer.setEnabled(true); - - userNameField.grabFocus(); - } - }); - - doneButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent event) { - if(machineIDField.getText().equals("")) { - machineIDField.setText("Field needed"); - return; - } - - sshContact.savePersistentDetails(); - - //add contact to contact list - ((OperationSetPersistentPresenceSSHImpl)sshContact - .getParentPresenceOperationSet()) - .addContactToList( - sshContact.getParentContactGroup(), - sshContact); - - setVisible(false); - } - }); - } - - /** - * Return the ssh icon - * - * @return the ssh icon - */ - public byte[] getIcon() { - return Resources.getImage(Resources.SSH_LOGO); - } - -// -// public void setContactGroup(ContactGroup contactGroup) -// { -// this.contactGroup = contactGroup; -// } -// -// public ContactGroup getContactGroup() -// { -// return this.contactGroup; -// } - - /** - * Sets the UserName of the dialog - * - * @param userName to be associated - */ - public void setUserNameField(String userName) { - this.userNameField.setText(userName); - } - - /** - * Sets the Password of the dialog - * - * @param password to be associated - */ - public void setPasswordField(String password) { - this.passwordField.setText(password); - } - - /** - * Return the hostname - * - * @return the hostname - */ - public String getHostName() { - return this.machineIDField.getText(); - } - - /** - * Return the username - * - * @return the username - */ - public String getUserName() { - return this.userNameField.getText(); - } - - /** - * Return the password - * - * @return the password in a clear form - */ - public String getPassword() { - return this.passwordField.getText(); - } - - /** - * Return the terminal type - * - * @return the terminal type - */ - public String getTerminalType() { - return this.terminalTypeField.getText(); - } - - /** - * Return the port - * - * @return the port value - */ - public int getPort() { - return Integer.parseInt(this.portField.getText().trim()); - } - - /** - * Return the update interval - * - * @return the update interval - */ - public int getUpdateInterval() { - return Integer.parseInt(String.valueOf(this.updateTimer.getValue())); - } - - /** - * Sets the HostName of the dialog - * - * @param hostName to be associated - */ - public void setHostNameField(String hostName) { - this.machineIDField.setText(hostName); - } - - /** - * Sets the Terminal Type of the dialog - * - * @param termType to be associated - */ - public void setTerminalType(String termType) { - this.terminalTypeField.setText(termType); - } - - /** - * Sets the Update Interval of the dialog - * - * @param interval to be associated - */ - public void setUpdateInterval(int interval) { - this.updateTimer.setValue(interval); - } - - /** - * Sets the Port of the dialog - * - * @param port to be associated - */ - public void setPort(String port) { - this.portField.setText(port); - } - - @Override - protected void close(boolean isEscaped) - { - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHFileTransferDaemon.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHFileTransferDaemon.java deleted file mode 100644 index 3b522ed..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHFileTransferDaemon.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; - -import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.Logger; - -import com.jcraft.jsch.*; - -/** - * @author Shobhit Jindal - */ -public class SSHFileTransferDaemon - extends Thread -{ - private static final Logger logger = - Logger.getLogger(SSHFileTransferDaemon .class); - - /** - * The contact of the remote machine - */ - private ContactSSH sshContact; - - /** - * The currently valid ssh protocol provider - */ - private ProtocolProviderServiceSSHImpl ppService; - - /** - * JSch Channel to be used for file transfer - */ - private Channel fileTransferChannel; - - /** - * The identifier for the Input Stream associated with SCP Channel - */ - private InputStream scpInputStream = null; - - /** - * The identifier for the Output Stream associated with SCP Channel - */ - private OutputStream scpOutputStream = null; - - /** - * Identifier of local file - */ - private String localPath; - - /** - * Identifier of remote file - */ - private String remotePath; - - /** - * File to be uploaded or saved - */ - private File file; - - /** - * The file input stream associated with the file to be uploaded - */ - private FileInputStream fileInputStream; - - /** - * The file output stream associated with the file to be uploaded - */ - private FileOutputStream fileOutputStream; - - /** - * The boolean which determines whether we are uploading or downloading - * files - */ - private boolean uploadFile; - - /** - * The currently valid ssh persistent presence operation set - */ - private OperationSetPersistentPresenceSSHImpl opSetPersPresence = null; - - /** - * The currently valid ssh instant messaging operation set - */ - private OperationSetBasicInstantMessagingSSHImpl instantMessaging = null; - - /** - * Creates a new instance of SSHFileTransferDaemon - * - * - * @param sshContact The contact of the remote machine - * @param ppService The current ssh protocol provider - */ - public SSHFileTransferDaemon( - ContactSSH sshContact, - ProtocolProviderServiceSSHImpl ppService) - { - super(); - this.sshContact = sshContact; - this.opSetPersPresence = (OperationSetPersistentPresenceSSHImpl) - ppService.getOperationSet(OperationSetPersistentPresence.class); - this.instantMessaging = (OperationSetBasicInstantMessagingSSHImpl) - ppService.getOperationSet( - OperationSetBasicInstantMessaging.class); - this.ppService = ppService; - } - - /** - * This method is called when file is to be transfered from local machine - * to remote machine - * - * @param remotePath - the identifier for the remote file - * @param localPath - the identifier for the local file - */ - public void uploadFile( - String remotePath, - String localPath) - { - this.uploadFile = true; - this.remotePath = remotePath; - this.localPath = localPath; - - file = new File(localPath); - - start(); - } - - /** - * This method is called when a file is to be downloaded from remote machine - * to local machine - * - * @param remotePath - the identifier for the remote file - * @param localPath - the identifier for the local file - */ - public void downloadFile( - String remotePath, - String localPath) - { - this.uploadFile = false; - this.remotePath = remotePath; - this.localPath = localPath; - - file = new File(localPath); - - start(); - } - - /** - * Background thread for the file transfer - */ - @Override - public void run() - { - //oldStatus to be resumed earlier - PresenceStatus oldStatus = sshContact.getPresenceStatus(); - - opSetPersPresence.changeContactPresenceStatus( - sshContact, - SSHStatusEnum.CONNECTING); - - try - { - //create a new JSch session if current is invalid - if( !ppService.isSessionValid(sshContact)) - ppService.createSSHSessionAndLogin(sshContact); - - fileTransferChannel = sshContact.getSSHSession() - .openChannel("exec"); - String command; - - // -p = Preserves modification times, access times, and modes from - // the original file - if(uploadFile) - command = "scp -p -t " + remotePath; - else - command = "scp -f " + remotePath; - - //the command to be executed on the remote terminal - ((ChannelExec)fileTransferChannel).setCommand(command); - - scpInputStream = fileTransferChannel.getInputStream(); - scpOutputStream = fileTransferChannel.getOutputStream(); - - fileTransferChannel.connect(); - - //file transfer is setup - opSetPersPresence.changeContactPresenceStatus( - sshContact, - SSHStatusEnum.FILE_TRANSFER); - - if(uploadFile) - { - instantMessaging.deliverMessage( - instantMessaging.createMessage( - "Uploading " + file.getName() + " to server"), - sshContact); - - upload(); - } - else - { - instantMessaging.deliverMessage( - instantMessaging.createMessage( - "Downloading " + file.getName() + " from server"), - sshContact); - - download(); - } - - } - catch(Exception ex) - { - //presently errors(any type) are directly logged directly in chat - instantMessaging.deliverMessage( - instantMessaging.createMessage(ex.getMessage()), - sshContact); - - logger.error(ex.getMessage()); - - try - { - if(fileInputStream!=null) - { - fileInputStream.close(); - } - - if(fileOutputStream!=null) - { - fileOutputStream.close(); - } - } - catch(Exception e) - {} - } - - // restore old status - opSetPersPresence.changeContactPresenceStatus( - sshContact, - oldStatus); - } - - /** - * Check for error in reading stream of remote machine - * - * @return 0 for success, 1 for error, 2 for fatal error, -1 otherwise - * @throws IOException when the network goes down - */ - private int checkAck(InputStream inputStream) - throws IOException - { - int result = inputStream.read(); - - // read error message - if(result==1 || result==2) - { - StringBuffer buffer = new StringBuffer(); - - int ch; - - do - { - //read a line of message - ch = inputStream.read(); - buffer.append((char)ch); - - }while(ch != '\n'); - - ProtocolProviderServiceSSHImpl - .getUIService() - .getPopupDialog() - .showMessagePopupDialog( - buffer.toString(), - "File Transfer Error: " - + sshContact.getDisplayName(), - PopupDialog.ERROR_MESSAGE); - - logger.error(buffer.toString()); - } - - return result; - } - - /** - * Uploads the file to the remote server - * - * @throws IOException when the network goes down - * @throws OperationFailedException when server behaves unexpectedly - */ - private void upload() - throws IOException, - OperationFailedException - { - fileInputStream = new FileInputStream(file); - - byte[] buffer = new byte[1024]; - int result, bytesRead; - - if( (result = checkAck(scpInputStream)) !=0) - throw new OperationFailedException("Error in Ack", result); - - // send "C0644 filesize filename", where filename should not include '/' - long filesize= file.length(); - String command = "C0644 " + filesize + " "; - -// if(lfile.lastIndexOf('/')>0) -// { -// command+=lfile.substring(lfile.lastIndexOf('/')+1); -// } -// else -// { -// command+=lfile; -// } - - command += file.getName() + "\n"; - if (logger.isTraceEnabled()) - logger.trace(command); - scpOutputStream.write(command.getBytes()); - scpOutputStream.flush(); - - if( (result = checkAck(scpInputStream)) !=0) - throw new OperationFailedException("Error in Ack", result); - - while(true) - { - bytesRead = fileInputStream.read(buffer, 0, buffer.length); - if(bytesRead <= 0) - break; - - scpOutputStream.write(buffer, 0, bytesRead); //out.flush(); - } - fileInputStream.close(); - fileInputStream = null; - - // send '\0' - buffer[0]=0; scpOutputStream.write(buffer, 0, 1); - scpOutputStream.flush(); - - if( (result = checkAck(scpInputStream)) !=0) - throw new OperationFailedException("Error in Ack", result); - - scpInputStream.close(); - scpOutputStream.close(); - - fileTransferChannel.disconnect(); - - instantMessaging.deliverMessage( - instantMessaging.createMessage(file.getName() - + " uploaded to Server"), - sshContact); - } - - /** - * Downloads a file from the remote machine - * - * @throws IOException when the network goes down - * @throws OperationFailedException when server behaves unexpectedly - */ - private void download() - throws IOException, - OperationFailedException - { - fileOutputStream = new FileOutputStream(file); - - int result; - - byte[] buffer = new byte[1024]; - - // send '\0' - buffer[0]=0; - - scpOutputStream.write(buffer, 0, 1); - scpOutputStream.flush(); - - int ch = checkAck(scpInputStream); - - if(ch!='C') - { - throw new OperationFailedException("Invalid reply from server", 12); - } - - // read '0644 ' - scpInputStream.read(buffer, 0, 5); - - long filesize=0L; - while(true) - { - if(scpInputStream.read(buffer, 0, 1) < 0) - { - // error - break; - } - if(buffer[0]==' ')break; - filesize=filesize*10L+buffer[0]-'0'; - } - - String file=null; - for(int i=0;true;i++) - { - scpInputStream.read(buffer, i, 1); - if(buffer[i]==(byte)0x0a) - { - file=new String(buffer, 0, i); - break; - } - } - - //System.out.println("filesize="+filesize+", file="+file); - - // send '\0' - buffer[0]=0; - scpOutputStream.write(buffer, 0, 1); - scpOutputStream.flush(); - - // read a content of lfile - int foo; - while(true) - { - if(buffer.length<filesize) - foo=buffer.length; - else - foo=(int)filesize; - - foo = scpInputStream.read(buffer, 0, foo); - if(foo<0) - break; - - fileOutputStream.write(buffer, 0, foo); - filesize-=foo; - if(filesize==0L) break; - } - fileOutputStream.close(); - fileOutputStream=null; - - if( (result = checkAck(scpInputStream)) !=0) - throw new OperationFailedException("Error in Ack", result); - - // send '\0' - buffer[0]=0; - scpOutputStream.write(buffer, 0, 1); - scpOutputStream.flush(); - - scpInputStream.close(); - scpOutputStream.close(); - - fileTransferChannel.disconnect(); - - instantMessaging.deliverMessage( - instantMessaging.createMessage( - this.file.getName() + " downloaded from Server"), - sshContact); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHReaderDaemon.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHReaderDaemon.java deleted file mode 100644 index 2c33b5d..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHReaderDaemon.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.io.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * - * @author Shobhit Jindal - */ -public class SSHReaderDaemon - extends Thread -{ - - /** - * A Buffer to aggregate replies to be sent as one message - */ - private StringBuffer replyBuffer; - - /** - * The identifier of Contact representing the remote machine - */ - private ContactSSHImpl sshContact; - - /** - * The identifier of the message received from server - */ - private String message; - - /** - * An identifier representing the state of Reader Daemon - */ - private boolean isActive = false; - - /** - * This OperationSet delivers incoming message - */ - private OperationSetBasicInstantMessagingSSHImpl instantMessaging; - - /** - * Input Stream of remote user to be read - */ - private InputStream shellInputStream; - - /** - * Buffered Reader associated with above input stream - */ - private InputStreamReader shellReader; - -// /** -// * This OperationSet delivers incoming message -// */ -// private OperationSetPersistentPresenceSSHImpl persistentPresence; - - /** - * Bytes available in Input Stream before reading - */ - private int bytesAvailable; - - private int bytesRead; - - int bufferCount; - - char buf; - - /** - * Creates a new instance of SSHReaderDaemon - */ - public SSHReaderDaemon(ContactSSH sshContact) - { - this.sshContact = (ContactSSHImpl)sshContact; - instantMessaging = - (OperationSetBasicInstantMessagingSSHImpl) - sshContact - .getProtocolProvider() - .getOperationSet( - OperationSetBasicInstantMessaging.class); - } - - /** - * Reads the remote machine, updating the chat window as necessary - * in a background thread - */ - @Override - public void run() - { - shellInputStream = sshContact.getShellInputStream(); - shellReader = sshContact.getShellReader(); - replyBuffer = new StringBuffer(); - - - try - { - do - { - bytesAvailable = shellInputStream.available(); - - if(bytesAvailable == 0 ) - { - // wait if more data is available - // for a slower connection this value need to be raised - // to avoid splitting of messages - Thread.sleep(250); - continue; - } - - bufferCount = 0; - -// if(replyBuffer > 0) - - do - { - // store the responses in a buffer - storeMessage(replyBuffer); - - Thread.sleep(250); - - bytesAvailable = shellInputStream.available(); - - }while(bytesAvailable > 0 && bufferCount<16384); - - message = replyBuffer.toString(); - - if(sshContact.isCommandSent()) - { - // if the response is as a result of a command sent - sshContact.setMessageType( - ContactSSH.CONVERSATION_MESSAGE_RECEIVED); - - message = message.substring(message.indexOf('\n') + 1); - - sshContact.setCommandSent(false); - } - else - { - // server sent an asynchronous message to the terminal - // display it as a system message - sshContact.setMessageType( - ContactSSH.SYSTEM_MESSAGE_RECEIVED); - - //popup disabled -// JOptionPane.showMessageDialog( -// null, -// message, -// "Message from " + sshContact.getDisplayName(), -// JOptionPane.INFORMATION_MESSAGE); - } - - instantMessaging.deliverMessage( - instantMessaging.createMessage(message), - sshContact); - - replyBuffer.delete(0, replyBuffer.length()); - - }while(isActive); - } - catch(Exception ex) - { - ex.printStackTrace(); - } - } - - /** - * Stores the response from server in a temporary buffer - * the bytes available are determined before the function is called - * - * @param replyBuffer to store the response from server - * - * @throws IOException if the network goes down - */ - private void storeMessage(StringBuffer replyBuffer) throws IOException - { - do - { - buf = (char)shellInputStream.read(); - -// System.out.println(String.valueOf(buf)+ " " + (int)buf); - - replyBuffer.append(String.valueOf(buf)); - -// logger.debug(shellReader.readLine()); - - bufferCount++; - - bytesAvailable--; - - }while(bytesAvailable>0 && bufferCount<32700); - } - - public void isActive(boolean isActive) - { - this.isActive = isActive; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHStatusEnum.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHStatusEnum.java deleted file mode 100644 index 0877399..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHStatusEnum.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.ssh; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * An implementation of <tt>PresenceStatus</tt> that enumerates all states that - * a SSH contact can fall into. - * - * @author Shobhit Jindal - */ -public class SSHStatusEnum - extends PresenceStatus -{ - - /** - * Indicates an Offline status or status with 0 connectivity. - */ - public static final SSHStatusEnum OFFLINE - = new SSHStatusEnum( - 0, - "Offline", - ProtocolIconSSHImpl - .getImageInBytes("service.protocol.ssh.OFFLINE_STATUS_ICON")); - - /** - * The Not Available status. Indicates that the user has connectivity - * but might not be able to immediately act (i.e. even less immediately - * than when in an Away status ;-P ) upon initiation of communication. - * - */ - public static final SSHStatusEnum NOT_AVAILABLE - = new SSHStatusEnum( - 35, - "Not Available", - ProtocolIconSSHImpl - .getImageInBytes("service.protocol.ssh.NA_STATUS_ICON")); - - /** - * The Connecting status. Indicate that the user is connecting to remote - * server - */ - public static final SSHStatusEnum CONNECTING - = new SSHStatusEnum( - 55, - "Connecting", - ProtocolIconSSHImpl - .getImageInBytes("service.protocol.ssh.CONNECTING_ICON")); - - /** - * The Online status. Indicate that the user is able and willing to - * communicate. - */ - public static final SSHStatusEnum ONLINE - = new SSHStatusEnum( - 65, - "Online", - ProtocolIconSSHImpl - .getImageInBytes("service.protocol.ssh.SSH_16x16")); - - - /** - * The Connecting status. Indicate that the user is connecting to remote - * server - */ - public static final SSHStatusEnum CONNECTED - = new SSHStatusEnum( - 70, - "Connecting", - ProtocolIconSSHImpl - .getImageInBytes("service.protocol.ssh.CONNECTED_ICON")); - - /** - * The File Transfer status. Indicate that the user is transfering a file - * to/from a remote server - */ - public static final SSHStatusEnum FILE_TRANSFER - = new SSHStatusEnum( - 75, - "Transfering File", - ProtocolIconSSHImpl - .getImageInBytes("service.protocol.ssh.FILE_TRANSFER_ICON")); - - /** - * Initialize the list of supported status states. - */ - private static List<PresenceStatus> supportedStatusSet = new LinkedList<PresenceStatus>(); - static - { - supportedStatusSet.add(OFFLINE); -// supportedStatusSet.add(NOT_AVAILABLE); - supportedStatusSet.add(ONLINE); -// supportedStatusSet.add(CONNECTING); - } - - /** - * Creates an instance of <tt>SSHPresneceStatus</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 SSHStatusEnum(int status, - String statusName, - byte[] statusIcon) - { - super(status, statusName, statusIcon); - } - - /** - * Returns an iterator over all status instances supproted by the ssh - * provider. - * @return an <tt>Iterator</tt> over all status instances supported by the - * ssh provider. - */ - static Iterator<PresenceStatus> supportedStatusSet() - { - return supportedStatusSet.iterator(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHUserInfo.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHUserInfo.java deleted file mode 100644 index 082b05f..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHUserInfo.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.java.sip.communicator.impl.protocol.ssh; - -import javax.swing.*; - -import com.jcraft.jsch.*; - -/** - * SSHUserInfo passes authentication details to JSch SSH Stack - * - * @author Shobhit Jindal - */ -class SSHUserInfo - implements UserInfo, - UIKeyboardInteractive -{ - /** - * The Contact of the remote machine - */ - private ContactSSH sshContact; - - /** - * Identifier for failure of authentication - * more explanation below in promptPassword function - */ - private boolean failedOnce = false; - - /** - * Password field for requesting auth details from user - */ - JTextField passwordField=new JPasswordField(20); - - /** - * Creates a UserInfo instance - * - * @param sshContact the contact concerned - */ - SSHUserInfo(ContactSSH sshContact) - { - this.sshContact = sshContact; - } - - /** - * Returns the password of account associated with this contact - * - * @return the password of account associated with this contact - */ - public String getPassword() - { - return sshContact.getPassword(); - } - - /** - * Prompt for accepting the cipher information of the remote server - * - * @param str the string to display - * - * @return the user's answer - */ - public boolean promptYesNo(String str) - { - Object[] options={ "yes", "no" }; - int foo=JOptionPane.showOptionDialog(null, - str, - "Warning", - JOptionPane.DEFAULT_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, options, options[0]); - return foo==0; - } - - /** - * Passphrase authentication presently not implemented - * - * @return null - */ - public String getPassphrase() - { return null; } - - /** - * Passphrase authentication presently not implemented - * - * @return true - */ - public boolean promptPassphrase(String message) - { return true; } - - /** - * Asks user to re-enter password information in case of an auth failure - * - * @param message the message to display - * - * @return the user's answer - */ - public boolean promptPassword(String message) - { - /** - * Auth always fails for the first time for Redhat based machines. - * Trying again with the same password - */ - if(!failedOnce) - { - failedOnce = true; - return true; - } - - Object[] ob={passwordField}; - int result=JOptionPane.showConfirmDialog(null, ob, "Auth Failed: " - + message, - JOptionPane.OK_CANCEL_OPTION); - - if(result==JOptionPane.OK_OPTION) - { - sshContact.setPassword(passwordField.getText()); - return true; - } - - return false; - } - - /** - * Shows a message from server - * - * @param message The message to display - */ - public void showMessage(String message) - { - JOptionPane.showMessageDialog(null, message); - } - - /** - * Keyboard Interactive Auth - not implemented - */ - public String[] promptKeyboardInteractive( - String destination, - String name, - String instruction, - String[] prompt, - boolean[] echo) - { - String response[] = new String[prompt.length]; - response[0] = sshContact.getPassword(); - return response; - } - - -} diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ssh.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/ssh/ssh.provider.manifest.mf deleted file mode 100644 index eb9472f..0000000 --- a/src/net/java/sip/communicator/impl/protocol/ssh/ssh.provider.manifest.mf +++ /dev/null @@ -1,20 +0,0 @@ -Bundle-Activator: net.java.sip.communicator.impl.protocol.ssh.SSHActivator -Bundle-Name: SSH Protocol Provider -Bundle-Description: A bundle providing support for the SSH protocol. -Bundle-Vendor: jitsi.org -Bundle-Version: 0.0.1 -Bundle-SymbolicName: net.java.sip.communicator.protocol.ssh -Import-Package: org.osgi.framework, - javax.crypto, - javax.crypto.interfaces, - javax.crypto.spec, - javax.swing, - javax.swing.border, - javax.swing.text, - org.jitsi.service.configuration, - net.java.sip.communicator.service.gui, - net.java.sip.communicator.service.protocol, - net.java.sip.communicator.service.protocol.event, - org.jitsi.service.resources, net.java.sip.communicator.service.resources, - net.java.sip.communicator.util, - net.java.sip.communicator.plugin.desktoputil diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java deleted file mode 100644 index 0514c16..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import net.java.sip.communicator.service.protocol.*; - -/** - * The Yahoo implementation of the service.protocol.ContactGroup interface. There - * are two types of groups possible here. <tt>RootContactGroupYahooImpl</tt> - * which is the root node of the ContactList itself and - * <tt>ContactGroupYahooImpl</tt> which represents standard groups. The - * reason for having those 2 is that generally, Yahoo groups may not contain - * subgroups. A contact list on the other hand may not directly contain buddies. - * - * - * The reason for having an abstract class is only - being able to esily - * recognize our own (Yahoo) contacts. - * @author Damian Minkov - */ -public abstract class AbstractContactGroupYahooImpl - implements ContactGroup -{ - - -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomInvitationYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomInvitationYahooImpl.java deleted file mode 100644 index e849f6c..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomInvitationYahooImpl.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import net.java.sip.communicator.service.protocol.*; - -/** - * The Yahoo implementation of the <tt>AdHocChatRoomInvitation</tt> interface. - * - * @author Rupert Burchardi - * @author Valentin Martinet - */ -public class AdHocChatRoomInvitationYahooImpl - implements AdHocChatRoomInvitation -{ - /** - * Corresponding chat room instance. - */ - private AdHocChatRoom chatRoom; - /** - * The name of the inviter - */ - private String inviter; - - /** - * The invitation reason. - */ - private String reason; - - - /** - * Creates an instance of the <tt>ChatRoomInvitationMsnImpl</tt> by - * specifying the targetChatRoom, the inviter, the reason. - * - * @param targetChatRoom The <tt>AdHocChatRoom</tt> for which the invitation - * is - * @param inviter The <tt>Contact</tt>, which sent the invitation - * @param reason The Reason for the invitation - */ - public AdHocChatRoomInvitationYahooImpl( AdHocChatRoom targetChatRoom, - String inviter, - String reason) - { - this.chatRoom = targetChatRoom; - this.inviter = inviter; - this.reason = reason; - } - - /** - * Returns the corresponding chat room. - * @return The ad-hoc chat room - */ - public AdHocChatRoom getTargetAdHocChatRoom() - { - return chatRoom; - } - - /** - * Returns the corresponding inviter. - * @return The name of the inviter - */ - public String getInviter() - { - return inviter; - } - - /** - * Returns the invitation reason. - * @return the invitation reason - */ - public String getReason() - { - return reason; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomYahooImpl.java deleted file mode 100644 index 2775f6f..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomYahooImpl.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; -import ymsg.network.*; - -/** - * Represents a Yahoo ad-hoc chat room, where multiple chat users could - * communicate in a many-to-many fashion. - * - * @author Rupert Burchardi - * @author Valentin Martinet - */ -public class AdHocChatRoomYahooImpl - implements AdHocChatRoom -{ - private static final Logger logger = Logger - .getLogger(AdHocChatRoomYahooImpl.class); - - /** - * Listeners that will be notified of changes in member status in the room - * such as member joined, left or being kicked or dropped. - */ - private Vector<AdHocChatRoomParticipantPresenceListener> memberListeners - = new Vector<AdHocChatRoomParticipantPresenceListener>(); - - /** - * Listeners that will be notified every time a new message is received on - * this ad-hoc chat room. - */ - private Vector<AdHocChatRoomMessageListener> messageListeners - = new Vector<AdHocChatRoomMessageListener>(); - - /** - * The protocol provider that created us - */ - private ProtocolProviderServiceYahooImpl provider = null; - - /** - * The operation set that created us. - */ - private OperationSetAdHocMultiUserChatYahooImpl opSetMuc = null; - - /** - * The list of participants of this chat room. - */ - private Hashtable<String, Contact> participants - = new Hashtable<String, Contact>(); - - /** - * The nickname of this chat room local user participant. - */ - private String nickname; - - /** - * The yahoo conference model of this ad-hoc chat room, its the - * representation of an ad-hoc chat room in the lib for this protocol. - */ - private YahooConference yahooConference = null; - - /** - * Creates an instance of a chat room that has been. - * - * @param multiUserChat - * MultiUserChat - * @param provider - * a reference to the currently valid jabber protocol provider. - */ - public AdHocChatRoomYahooImpl( YahooConference multiUserChat, - ProtocolProviderServiceYahooImpl provider) - { - this.yahooConference = multiUserChat; - this.provider = provider; - this.opSetMuc = (OperationSetAdHocMultiUserChatYahooImpl) provider - .getOperationSet(OperationSetAdHocMultiUserChat.class); - } - - /** - * Registers <tt>listener</tt> so that it would receive events every time a - * new message is received on this chat room. - * - * @param listener A <tt>MessageListener</tt> that would be notified every - * time a new message is received on this chat room. - */ - public void addMessageListener(AdHocChatRoomMessageListener listener) - { - synchronized (messageListeners) - { - if (!messageListeners.contains(listener)) - messageListeners.add(listener); - } - } - - /** - * Removes <tt>listener</tt> so that it won't receive any further message - * events from this room. - * - * @param listener The <tt>MessageListener</tt> to remove from this room - */ - public void removeMessageListener(AdHocChatRoomMessageListener listener) - { - synchronized (messageListeners) - { - messageListeners.remove(listener); - } - } - - /** - * Adds a listener that will be notified of changes in our status in the - * room. - * - * @param listener A participant status listener. - */ - public void addParticipantPresenceListener( - AdHocChatRoomParticipantPresenceListener listener) - { - synchronized (memberListeners) - { - if (!memberListeners.contains(listener)) - memberListeners.add(listener); - } - } - - /** - * Removes a listener that was being notified of changes in the status of - * other chat room participants. - * - * @param listener A participant status listener. - */ - public void removeParticipantPresenceListener( - AdHocChatRoomParticipantPresenceListener listener) - { - synchronized (memberListeners) - { - memberListeners.remove(listener); - } - } - - /** - * 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) - { - Message msg = new MessageYahooImpl(messageText, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE, - OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING, null); - return msg; - } - - /** - * Returns a <tt>List</tt> of <tt>Contact</tt>s corresponding to all members - * currently participating in this room. - * - * @return a <tt>List</tt> of <tt>Contact</tt> corresponding to all room - * members. - */ - public List<Contact> getParticipants() - { - return new LinkedList<Contact>(participants.values()); - } - - /** - * Updates the member list of the chat room. - * - */ - public void updateParticipantsList() - { - Iterator<?> it = yahooConference.getMembers().iterator(); - - while (it.hasNext()) - { - YahooUser user = (YahooUser) it.next(); - Contact contact; - OperationSetPersistentPresenceYahooImpl presenceOpSet - = (OperationSetPersistentPresenceYahooImpl) this - .getParentProvider().getOperationSet( - OperationSetPersistentPresence.class); - - contact = presenceOpSet.findContactByID(user.getId()); - - if(!participants.containsKey(contact.getDisplayName())) - { - participants.put(contact.getDisplayName(), contact); - } - } - } - - /** - * Returns the identifier of this <tt>AdHocChatRoom</tt>. - * - * @return a <tt>String</tt> containing the identifier of this - * <tt>AdHocChatRoom</tt>. - */ - public String getIdentifier() - { - return yahooConference.getName(); - } - - /** - * Returns the number of participants that are currently in this ad-hoc chat - * room. - * - * @return the number of <tt>Contact</tt>s, currently participating in - * this ad-hoc room. - */ - public int getParticipantsCount() - { - return yahooConference.getMembers().size(); - } - - /** - * Returns the name of this <tt>AdHocChatRoom</tt>. - * - * @return a <tt>String</tt> containing the name of this - * <tt>AdHocChatRoom</tt>. - */ - public String getName() - { - return yahooConference.getName(); - } - - /** - * Returns the protocol provider service that created us. - * - * @return the protocol provider service that created us. - */ - public ProtocolProviderService getParentProvider() - { - return provider; - } - - /** - * Returns the local user's nickname in the context of this chat room or - * <tt>null</tt> if not currently joined. - * - * @return the nickname currently being used by the local user in the - * context of the local ad-hoc chat room. - */ - - public String getUserNickname() - { - if(nickname == null) - nickname = provider.getYahooSession().getLoginIdentity().getId(); - - return nickname; - } - - /** - * Invites another user to this room. If we're not joined nothing will - * happen. - * - * @param userAddress The identifier of the contact (email address or yahoo - * id) - * @param reason The invite reason, which is send to the invitee. - */ - public void invite(String userAddress, String reason) - { - try - { - provider.getYahooSession().extendConference(yahooConference, - userAddress, reason); - } - catch (IOException ioe) - { - if (logger.isDebugEnabled()) - logger.debug("Failed to invite the user: " + userAddress - + " Error: " + ioe); - } - } - - /** - * Indicates whether or not this chat room is corresponding to a server - * channel. Note: Returns always <code>false</code>. - * - * @return Always <code>false</code> since system chat room can't be joined - * with current yahoo library. - */ - public boolean isSystem() - { - return false; - } - - /** - * Joins this chat room with the nickname of the local user so that the user - * would start receiving events and messages for it. - * - * @throws OperationFailedException with the corresponding code if an error - * occurs while joining the room. - */ - public void join() throws OperationFailedException - { - this.nickname = provider.getAccountID().getUserID(); - try - { - provider.getYahooSession().acceptConferenceInvite(yahooConference); - - // We don't specify a reason. - opSetMuc.fireLocalUserPresenceEvent(this, - LocalUserAdHocChatRoomPresenceChangeEvent.LOCAL_USER_JOINED, - null); - } - catch (Exception e) - { - if (logger.isDebugEnabled()) - logger.debug("Couldn't join the chat room: " - + yahooConference.getName() + e); - } - } - - /** - * Leave this chat room. Once this method is called, the user won't be - * listed as a member of the chat room any more and no further chat events - * will be delivered. Depending on the underlying protocol and - * implementation leave() might cause the room to be destroyed if it has - * been created by the local user. - */ - public void leave() - { - try - { - provider.getYahooSession().leaveConference(yahooConference); - - Iterator< Map.Entry<String, Contact>> membersSet - = participants.entrySet().iterator(); - - while (membersSet.hasNext()) - { - Map.Entry<String, Contact> memberEntry = membersSet.next(); - Contact participant = memberEntry.getValue(); - - fireParticipantPresenceEvent(participant, - AdHocChatRoomParticipantPresenceChangeEvent.CONTACT_LEFT, - "Local user has left the chat room."); - } - } - catch (IOException ioe) - { - if (logger.isDebugEnabled()) - logger.debug("Failed to leave the chat room: " - + yahooConference.getName() + " Error: " + ioe); - } - - participants.clear(); - } - - /** - * Sends the <tt>message</tt> to the destination indicated by the - * <tt>to</tt> contact. - * - * @param message The <tt>Message</tt> to send. - * @throws OperationFailedException if the underlying stack is not - * registered or initialized or if the chat room is not joined. - */ - public void sendMessage(Message message) throws OperationFailedException - { - assertConnected(); - - try - { - provider.getYahooSession().sendConferenceMessage(yahooConference, - message.getContent()); - - AdHocChatRoomMessageDeliveredEvent msgDeliveredEvt - = new AdHocChatRoomMessageDeliveredEvent( - this, - new Date(), - message, - ChatRoomMessageDeliveredEvent.CONVERSATION_MESSAGE_DELIVERED); - - fireMessageEvent(msgDeliveredEvt); - } - catch (Exception e) - { - if (logger.isDebugEnabled()) - logger.debug("Failed to send a conference message."); - } - } - - /** - * Notifies all interested listeners that a - * <tt>AdHocChatRoomMessageDeliveredEvent</tt>, - * <tt>AdHocChatRoomMessageReceivedEvent</tt> or a - * <tt>AdHocChatRoomMessageDeliveryFailedEvent</tt> has been fired. - * @param evt The specific event - */ - public void fireMessageEvent(EventObject evt) - { - Iterator<AdHocChatRoomMessageListener> listeners = null; - synchronized (messageListeners) - { - listeners = new ArrayList<AdHocChatRoomMessageListener>( - messageListeners).iterator(); - } - - while (listeners.hasNext()) - { - AdHocChatRoomMessageListener listener = listeners.next(); - - if (evt instanceof AdHocChatRoomMessageDeliveredEvent) - { - listener.messageDelivered( - (AdHocChatRoomMessageDeliveredEvent) evt); - } - else if (evt instanceof AdHocChatRoomMessageReceivedEvent) - { - listener.messageReceived( - (AdHocChatRoomMessageReceivedEvent) evt); - } - else if (evt instanceof AdHocChatRoomMessageDeliveryFailedEvent) - { - listener.messageDeliveryFailed( - (AdHocChatRoomMessageDeliveryFailedEvent) evt); - } - } - } - - /** - * Creates the corresponding AdHocChatRoomParticipantPresenceChangeEvent and - * notifies all <tt>AdHocChatRoomParticipantPresenceListener</tt>s that a - * Contact has joined or left this <tt>AdHocChatRoom</tt>. - * - * @param participant the <tt>Contact</tt> that this - * @param eventID the identifier of the event - * @param eventReason the reason of the event - */ - public void fireParticipantPresenceEvent(Contact participant, String eventID, - String eventReason) - { - AdHocChatRoomParticipantPresenceChangeEvent evt - = new AdHocChatRoomParticipantPresenceChangeEvent(this, - participant, - eventID, - eventReason); - - if (logger.isTraceEnabled()) - logger.trace("Will dispatch the following ChatRoom event: " + evt); - - Iterator<AdHocChatRoomParticipantPresenceListener> listeners = null; - synchronized (memberListeners) - { - listeners = new ArrayList<AdHocChatRoomParticipantPresenceListener> - (memberListeners).iterator(); - } - - while (listeners.hasNext()) - { - AdHocChatRoomParticipantPresenceListener listener = listeners.next(); - - listener.participantPresenceChanged(evt); - } - } - - /** - * Finds the participant of this ad-hoc chat room corresponding to the - * given address. - * - * @param address the address to search for. - * @return the participant of this chat room corresponding to the given - * nick name. - */ - public Contact findParticipantForAddress(String address) - { - Iterator<Contact> participantsIter - = this.participants.values().iterator(); - - while (participantsIter.hasNext()) - { - Contact contact = participantsIter.next(); - - if (contact.getAddress().equals(address)) - { - return contact; - } - } - - return null; - } - - /** - * Removes the specified ad-hoc chat room participant from the participants - * list of this ad-hoc chat room. - * @param participant The member, who should be removed from the ad-hoc chat room - * participants list. - */ - public void removeChatRoomParticipant(Contact participant) - { - if(participant == null) - return; - - participants.remove(participant.getDisplayName()); - - fireParticipantPresenceEvent(participant, - AdHocChatRoomParticipantPresenceChangeEvent.CONTACT_LEFT, null); - } - - /** - * Adds a participant to the ad-hoc chat room participant list. - * @param participant The participant, who should be added to the ad-hoc - * chat room participant list. - */ - public void addChatRoomParticipant(Contact participant) - { - if (participant == null) - return; - - if (!participants.containsKey(participant.getDisplayName())) - { - participants.put(participant.getDisplayName(), participant); - - fireParticipantPresenceEvent(participant, - AdHocChatRoomParticipantPresenceChangeEvent.CONTACT_JOINED, - null); - } - } - - /** - * Returns the yahoo conference model of this chat room. - * @return The yahoo conference. - */ - public YahooConference getYahooConference() - { - return yahooConference; - } - - /** - * Utility method throwing an exception if the stack is not properly - * initialized. - * @throws java.lang.IllegalStateException if the underlying stack is - * not registered and initialized. - */ - private void assertConnected() throws IllegalStateException - { - if (provider == null) - throw new IllegalStateException( - "The provider must be non-null and signed on the " - +"service before being able to communicate."); - if (!provider.isRegistered()) - throw new IllegalStateException( - "The provider must be signed on the service before " - +"being able to communicate."); - } - - /** - * Determines whether this chat room should be stored in the configuration - * file or not. If the chat room is persistent it still will be shown after a - * restart in the chat room list. A non-persistent chat room will be only in - * the chat room list until the the program is running. - * - * @return true if this chat room is persistent, false otherwise - */ - public boolean isPersistent() - { - return false; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java deleted file mode 100644 index 1f73aac..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import ymsg.network.*; - -/** - * The Yahoo implementation of the ContactGroup interface. Intances of this class - * (contrary to <tt>RootContactGroupYahooImpl</tt>) may only contain buddies - * and cannot have sub groups. Note that instances of this class only use the - * corresponding smack source group for reading their names and only - * initially fill their <tt>buddies</tt> <tt>java.util.List</tt> with - * the ContactYahooImpl objects corresponding to those contained in the source - * group at the moment it is being created. They would, however, never try to - * sync or update their contents ulteriorly. This would have to be done through - * the addContact()/removeContact() methods. - * The content of buddies is created on creating of the group and when the smack - * source group is changed. - * - * @author Damian Minkov - * @author Emil Ivov - */ -public class ContactGroupYahooImpl - extends AbstractContactGroupYahooImpl -{ - private final Map<String, Contact> buddies - = new Hashtable<String, Contact>(); - - private boolean isResolved = false; - - /** - * The Yahoo Group corresponding to this contact group. - */ - private YahooGroup yahooGroup = null; - - /** - * a list that would always remain empty. We only use it so that we're able - * to extract empty iterators - */ - private final List<ContactGroup> dummyGroupsList - = new LinkedList<ContactGroup>(); - - private String tempId = null; - - private final ServerStoredContactListYahooImpl ssclCallback; - - /** - * Creates an Yahoo group using the specified <tt>YahooGroup</tt> as - * a source. The newly created group will always return the name of the - * underlying RosterGroup and would thus automatically adapt to changes. - * It would, however, not receive or try to poll for modifications of the - * buddies it contains and would therefore have to be updated manually by - * ServerStoredContactListImpl update will only be done if source group - * is changed. - - * @param yahooGroup the Yahoo Group correspoinding to the group - * @param groupMembers the group members that we should add to the group. - * @param ssclCallback a callback to the server stored contact list - * we're creating. - * @param isResolved a boolean indicating whether or not the group has been - * resolved against the server. - */ - ContactGroupYahooImpl( - YahooGroup yahooGroup, - Vector<YahooUser> groupMembers, - ServerStoredContactListYahooImpl ssclCallback, - boolean isResolved) - { - this.yahooGroup = yahooGroup; - this.isResolved = isResolved; - this.ssclCallback = ssclCallback; - - for (YahooUser yahooUser : groupMembers) - { - //only add the contact if it doesn't already exist in some other - //group. this would be necessary if Yahoo! one day start allowing - //the same contact in more than one group, which is not quite - //unlikely since most of the other protocols do it. - if(ssclCallback.findContactByYahooUser(yahooUser) != null) - { - continue; - } - - - addContact( - new ContactYahooImpl(yahooUser,ssclCallback, true, true)); - } - } - - ContactGroupYahooImpl( String id, - ServerStoredContactListYahooImpl ssclCallback) - { - this.tempId = id; - this.isResolved = false; - this.ssclCallback = ssclCallback; - } - - - /** - * Returns the number of <tt>Contact</tt> members of this - * <tt>ContactGroup</tt> - * - * @return an int indicating the number of <tt>Contact</tt>s, - * members of this <tt>ContactGroup</tt>. - */ - public int countContacts() - { - return buddies.size(); - } - - /** - * Returns a reference to the root group which in Yahoo is the parent of - * any other group since the protocol does not support subgroups. - * @return a reference to the root group. - */ - public ContactGroup getParentContactGroup() - { - return ssclCallback.getRootGroup(); - } - - /** - * Adds the specified contact to the end of this group. - * @param contact the new contact to add to this group - */ - void addContact(ContactYahooImpl contact) - { - buddies.put(contact.getAddress().toLowerCase(), contact); - } - - - /** - * Removes the specified contact from this contact group - * @param contact the contact to remove. - */ - void removeContact(ContactYahooImpl contact) - { - buddies.remove(contact.getAddress().toLowerCase()); - } - - /** - * Returns an Iterator over all contacts, member of this - * <tt>ContactGroup</tt>. - * - * @return a java.util.Iterator over all contacts inside this - * <tt>ContactGroup</tt>. In case the group doesn't contain any - * memebers it will return an empty iterator. - */ - public Iterator<Contact> contacts() - { - return buddies.values().iterator(); - } - - /** - * Returns the <tt>Contact</tt> with the specified address or - * identifier. - * @param id the addres or identifier of the <tt>Contact</tt> we are - * looking for. - * @return the <tt>Contact</tt> with the specified id or address. - */ - public Contact getContact(String id) - { - return this.findContact(id); - } - - /** - * Returns the name of this group. - * @return a String containing the name of this group. - */ - public String getGroupName() - { - if(isResolved) - return ServerStoredContactListYahooImpl - .replaceIllegalChars(yahooGroup.getName()); - else - return tempId; - } - - /** - * Determines whether the group may contain subgroups or not. - * - * @return always false since only the root group may contain subgroups. - */ - public boolean canContainSubgroups() - { - return false; - } - - /** - * Returns the subgroup with the specified index (i.e. always null since - * this group may not contain subgroups). - * - * @param index the index of the <tt>ContactGroup</tt> to retrieve. - * @return always null - */ - public ContactGroup getGroup(int index) - { - return null; - } - - /** - * Returns the subgroup with the specified name. - * @param groupName the name of the <tt>ContactGroup</tt> to retrieve. - * @return the <tt>ContactGroup</tt> with the specified index. - */ - public ContactGroup getGroup(String groupName) - { - return null; - } - - /** - * Returns an empty iterator. Subgroups may only be present in the root - * group. - * - * @return an empty iterator - */ - public Iterator<ContactGroup> subgroups() - { - return dummyGroupsList.iterator(); - } - - /** - * Returns the number of subgroups contained by this group, which is - * always 0 since sub groups in the protocol may only be contained - * by the root group - <tt>RootContactGroupImpl</tt>. - * @return a 0 int. - */ - public int countSubgroups() - { - return 0; - } - - /** - * Returns a hash code value for the object, which is actually the hashcode - * value of the groupname. - * - * @return a hash code value for this ContactGroup. - */ - @Override - public int hashCode() - { - return getGroupName().hashCode(); - } - - /** - * Indicates whether some other object is "equal to" this group. - * - * @param obj the reference object with which to compare. - * @return <tt>true</tt> if this object is the same as the obj - * argument; <tt>false</tt> otherwise. - */ - @Override - public boolean equals(Object obj) - { - if( obj == this ) - return true; - - if (obj == null - || !(obj instanceof ContactGroupYahooImpl) ) - return false; - - if(!((ContactGroup)obj).getGroupName().equals(getGroupName())) - return false; - - if(getProtocolProvider() != ((ContactGroup)obj).getProtocolProvider()) - return false; - - //since Yahoo does not support having two groups with the same name - // at this point we could bravely state that the groups are the same - // and not bother to compare buddies. (gotta check that though) - return true; - } - - /** - * Returns the protocol provider that this group belongs to. - * @return a reference to the ProtocolProviderService instance that this - * ContactGroup belongs to. - */ - public ProtocolProviderService getProtocolProvider() - { - return this.ssclCallback.getParentProvider(); - } - - /** - * Returns a string representation of this group, in the form - * YahooGroup.GroupName[size]{ buddy1.toString(), buddy2.toString(), ...}. - * @return a String representation of the object. - */ - @Override - public String toString() - { - StringBuffer buff = new StringBuffer("YahooGroup."); - buff.append(getGroupName()); - buff.append(", childContacts="+countContacts()+":["); - - Iterator<Contact> contacts = contacts(); - while (contacts.hasNext()) - { - Contact contact = contacts.next(); - buff.append(contact.toString()); - if(contacts.hasNext()) - buff.append(", "); - } - return buff.append("]").toString(); - } - - /** - * Returns the contact encapsulating with the spcieified name or - * null if no such contact was found. - * - * @param id the id for the contact we're looking for. - * @return the <tt>ContactYahooImpl</tt> corresponding to the specified - * screnname or null if no such contact existed. - */ - ContactYahooImpl findContact(String id) - { - if(id == null) - return null; - return (ContactYahooImpl)buddies.get(id.toLowerCase()); - } - - /** - * Determines whether or not this contact group is being stored by the - * server. Non persistent contact groups exist for the sole purpose of - * containing non persistent contacts. - * @return true if the contact group is persistent and false otherwise. - */ - public boolean isPersistent() - { - return true; - } - - /** - * Returns null as no persistent data is required and the contact address is - * sufficient for restoring the contact. - * <p> - * @return null as no such data is needed. - */ - public String getPersistentData() - { - return null; - } - - /** - * Determines whether or not this contact group has been resolved against - * the server. Unresolved group are used when initially loading a contact - * list that has been stored in a local file until the presence operation - * set has managed to retrieve all the contact list from the server and has - * properly mapped contacts and groups to their corresponding on-line - * buddies. - * @return true if the contact has been resolved (mapped against a buddy) - * and false otherwise. - */ - public boolean isResolved() - { - return isResolved; - } - - /** - * Resolve this contact group against the specified group - * @param yahooGroup the server stored group - */ - @SuppressWarnings("unchecked") //jymsg legacy code - void setResolved(YahooGroup yahooGroup) - { - if(isResolved) - return; - - this.isResolved = true; - - this.yahooGroup = yahooGroup; - - Vector<YahooUser> contacts = yahooGroup.getMembers(); - for (YahooUser item : contacts) - { - ContactYahooImpl contact = - ssclCallback.findContactById(item.getId()); - if(contact != null) - { - contact.setResolved(item); - - ssclCallback.fireContactResolved(this, contact); - } - else - { - ContactYahooImpl newContact = - new ContactYahooImpl(item, ssclCallback, true, true); - addContact(newContact); - - ssclCallback.fireContactAdded(this, newContact); - } - } - } - - /** - * Returns a <tt>String</tt> that uniquely represnets the group. In this we - * use the name of the group as an identifier. This may cause problems - * though, in clase the name is changed by some other application between - * consecutive runs of the sip-communicator. - * - * @return a String representing this group in a unique and persistent - * way. - */ - public String getUID() - { - return getGroupName(); - } - - /** - * The source group we are encapsulating - * @return YahooGroup - */ - YahooGroup getSourceGroup() - { - return yahooGroup; - } - - /** - * Change the source group - * change the buddies - * - * @param newGroup YahooGroup - */ - void setSourceGroup(YahooGroup newGroup) - { - this.yahooGroup = newGroup; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java deleted file mode 100644 index b23ee3d..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.yahooconstants.*; -import net.java.sip.communicator.util.*; -import ymsg.network.*; - -/** - * The Yahoo implementation of the service.protocol.Contact interface. - * @author Damian Minkov - * @author Emil Ivov - */ -public class ContactYahooImpl - extends AbstractContact -{ - private static final Logger logger = - Logger.getLogger(ContactYahooImpl.class); - - private YahooUser contact = null; - private byte[] image = null; - private PresenceStatus status = YahooStatusEnum.OFFLINE; - private ServerStoredContactListYahooImpl ssclCallback = null; - private boolean isPersistent = false; - private boolean isResolved = false; - private boolean isVolatile = false; - - private String yahooID = null; - private String id = null; - - private String statusMessage = null; - - /** - * Creates an YahooContactImpl with custom yahooID - * @param yahooID sets the contact Id if its different from the YahooUser id - * @param contact the contact object that we will be encapsulating. - * @param ssclCallback a reference to the ServerStoredContactListImpl - * instance that created us. - * @param isPersistent determines whether this contact is persistent or not. - * @param isResolved specifies whether the contact has been resolved against - * the server contact list - */ - ContactYahooImpl( - String yahooID, - YahooUser contact, - ServerStoredContactListYahooImpl ssclCallback, - boolean isPersistent, - boolean isResolved) - { - this.yahooID = yahooID; - - this.contact = contact; - this.ssclCallback = ssclCallback; - this.isPersistent = isPersistent; - this.isResolved = isResolved; - - if(contact != null) - id = contact.getId(); - else if(yahooID != null) - id = YahooSession.getYahooUserID(yahooID); - } - - /** - * Creates an YahooContactImpl - * @param contact the contact object that we will be encapsulating. - * @param ssclCallback a reference to the ServerStoredContactListImpl - * instance that created us. - * @param isPersistent determines whether this contact is persistent or not. - * @param isResolved specifies whether the contact has been resolved against - * the server contact list - */ - ContactYahooImpl( - YahooUser contact, - ServerStoredContactListYahooImpl ssclCallback, - boolean isPersistent, - boolean isResolved) - { - this(null, contact, ssclCallback, isPersistent, isResolved); - } - - /** - * Creates volatile or unresolved contact - */ - ContactYahooImpl( - String id, - ServerStoredContactListYahooImpl ssclCallback, - boolean isResolved, - boolean isPersistent, - boolean isVolatile) - { - this.yahooID = id; - this.ssclCallback = ssclCallback; - this.isPersistent = isPersistent; - this.isResolved = isResolved; - this.isVolatile = isVolatile; - - if(id != null) - this.id = YahooSession.getYahooUserID(yahooID); - } - - /** - * Returns the Yahoo Userid of this contact - * @return the Yahoo Userid of this contact - */ - public String getAddress() - { - // if the contact is volatile or with custom id return it - if(yahooID != null) - return yahooID; - // otherwise return the supplied contact id - else - return contact.getId(); - } - - /** - * Returns the custom yahooID if set - */ - String getYahooID() - { - return yahooID; - } - - /** - * Returns the contact Id. - * If contact missing the yahooID without @yahoo.com part is returned - */ - String getID() - { - return id; - } - - /** - * Returns whether the contact is volatile. - */ - boolean isVolatile() - { - return isVolatile; - } - - /** - * Returns an avatar if one is already present or <tt>null</tt> in case it - * is not in which case it the method also queues the contact for image - * updates. - * - * @return the avatar of this contact or <tt>null</tt> if no avatar is - * currently available. - */ - public byte[] getImage() - { - return getImage(true); - } - - /** - * Returns a reference to the image assigned to this contact. If no image - * is present and the retrieveIfNecessary flag is true, we schedule the - * image for retrieval from the server. - * - * @param retrieveIfNecessary specifies whether the method should queue - * this contact for avatar update from the server. - * - * @return a reference to the image currently stored by this contact. - */ - public byte[] getImage(boolean retrieveIfNecessary) - { - try - { - if(retrieveIfNecessary) - { - if(ssclCallback.getParentProvider() == null - || !ssclCallback.getParentProvider().isRegistered()) - { - throw new IllegalStateException( - "The provider must be signed on the service before " - +"being able to communicate."); - } - - YahooSession ses = ssclCallback.getParentProvider(). - getYahooSession(); - if(image == null && ses != null) - ses.requestPicture(id); - } - } - catch (Exception e) - { - if (logger.isInfoEnabled()) - logger.info("Error requesting image!", e); - } - - if(logger.isDebugEnabled()) - logger.debug("returning picture " + image); - - return image; - } - - /** - * Used to set the image of the contact if it is updated - * - * @param image a photo/avatar associated with this contact. - */ - protected void setImage(byte[] image) - { - if (logger.isInfoEnabled()) - logger.info("setting image " + image); - - this.image = image; - } - - /** - * Returns a string representation of this contact, containing most of its - * representative details. - * - * @return a string representation of this contact. - */ - @Override - public String toString() - { - StringBuffer buff = new StringBuffer("YahooContact[ id="); - buff.append(getAddress()).append("]"); - - return buff.toString(); - } - - /** - * Sets the status that this contact is currently in. The method is to - * only be called as a result of a status update received from the server. - * - * @param status the YahooStatusEnum that this contact is currently in. - */ - void updatePresenceStatus(PresenceStatus status) - { - this.status = status; - } - - /** - * Returns the status of the contact as per the last status update we've - * received for it. Note that this method is not to perform any network - * operations and will simply return the status received in the last - * status update message. If you want a reliable way of retrieving someone's - * status, you should use the <tt>queryContactStatus()</tt> method in - * <tt>OperationSetPresence</tt>. - * @return the PresenceStatus that we've received in the last status update - * pertaining to this contact. - */ - public PresenceStatus getPresenceStatus() - { - return status; - } - - /** - * Returns a String that could be used by any user interacting modules for - * referring to this contact. An alias is not necessarily unique but is - * often more human readable than an address (or id). - * @return a String that can be used for referring to this contact when - * interacting with the user. - */ - public String getDisplayName() - { - return getAddress(); - } - - /** - * Returns a reference to the contact group that this contact is currently - * a child of or null if the underlying protocol does not suppord persistent - * presence. - * @return a reference to the contact group that this contact is currently - * a child of or null if the underlying protocol does not suppord persistent - * presence. - */ - public ContactGroup getParentContactGroup() - { - return ssclCallback.findContactGroup(this); - } - - - /** - * Returns a reference to the protocol provider that created the contact. - * @return a refererence to an instance of the ProtocolProviderService - */ - public ProtocolProviderService getProtocolProvider() - { - return ssclCallback.getParentProvider(); - } - - /** - * Determines whether or not this contact is being stored by the server. - * Non persistent contacts are common in the case of simple, non-persistent - * presence operation sets. They could however also be seen in persistent - * presence operation sets when for example we have received an event - * from someone not on our contact list. Non persistent contacts are - * volatile even when coming from a persistent presence op. set. They would - * only exist until the application is closed and will not be there next - * time it is loaded. - * @return true if the contact is persistent and false otherwise. - */ - public boolean isPersistent() - { - return isPersistent; - } - - /** - * Specifies whether this contact is to be considered persistent or not. The - * method is to be used _only_ when a non-persistent contact has been added - * to the contact list and its encapsulated VolatileBuddy has been repalced - * with a standard buddy. - * @param persistent true if the buddy is to be considered persistent and - * false for volatile. - */ - void setPersistent(boolean persistent) - { - this.isPersistent = persistent; - } - - /** - * Resolve this contact against the given entry - * @param entry the server stored entry - */ - void setResolved(YahooUser entry) - { - if(isResolved) - return; - - this.isResolved = true; - contact = entry; - isVolatile = false; - } - - /** - * Returns the persistent data - * @return the persistent data - */ - public String getPersistentData() - { - return null; - } - - /** - * Determines whether or not this contact has been resolved against the - * server. Unresolved contacts are used when initially loading a contact - * list that has been stored in a local file until the presence operation - * set has managed to retrieve all the contact list from the server and has - * properly mapped contacts to their on-line buddies. - * @return true if the contact has been resolved (mapped against a buddy) - * and false otherwise. - */ - public boolean isResolved() - { - return isResolved; - } - - public void setPersistentData(String persistentData) - { - } - - /** - * Get source contact - * @return YahooContact - */ - YahooUser getSourceContact() - { - return contact; - } - - /** - * Return the current status message of this contact. - * - * @return the current status message - */ - public String getStatusMessage() - { - return statusMessage; - } - - /** - * Sets the current status message for this contact - * @param statusMessage the message - */ - protected void setStatusMessage(String statusMessage) - { - this.statusMessage = statusMessage; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/FileTransferImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/FileTransferImpl.java deleted file mode 100644 index 3f42079..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/FileTransferImpl.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * The Filetransfer imeplementation for yahoo. - * @author Damian Minkov - */ -public class FileTransferImpl - extends AbstractFileTransfer - -{ - private ProtocolProviderServiceYahooImpl yahooProvider; - private String id = null; - private Contact contact = null; - private File file = null; - private int direction = -1; - private long transferedBytes; - - public FileTransferImpl(ProtocolProviderServiceYahooImpl yahooProvider, - String id, Contact contact, File file, int direction) - { - this.yahooProvider = yahooProvider; - this.id = id; - this.contact = contact; - this.file = file; - this.direction = direction; - } - - /** - * Cancels this file transfer. When this method is called transfer should - * be interrupted. - */ - @Override - public void cancel() - { - yahooProvider.getYahooSession().cancelRunningFileTransfer(id); - } - - /** - * Returns the number of bytes already transfered through this file transfer. - * - * @return the number of bytes already transfered through this file transfer - */ - @Override - public long getTransferedBytes() - { - return transferedBytes; - } - - /** - * Uniquie ID that is identifying the FileTransfer - * if the request has been accepted. - * - * @return the id. - */ - public String getID() - { - return id; - } - - /** - * The file transfer direction. - * @return returns the direction of the file transfer : IN or OUT. - */ - public int getDirection() - { - return direction; - } - - /** - * Returns the file that is transfered. - * - * @return the file - */ - public File getLocalFile() - { - return file; - } - - /** - * Returns the contact that we are transfering files with. - * @return the contact. - */ - public Contact getContact() - { - return contact; - } - - /** - * @param transferedBytes the transferedBytes to set - */ - public void setTransferedBytes(long transferedBytes) - { - this.transferedBytes = transferedBytes; - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/IncomingFileTransferRequestYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/IncomingFileTransferRequestYahooImpl.java deleted file mode 100644 index 25561d0..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/IncomingFileTransferRequestYahooImpl.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; - -/** - * Implementation of the incoming file transfer request. - * - * @author Damian Minkov - */ -public class IncomingFileTransferRequestYahooImpl - implements IncomingFileTransferRequest -{ - private String id; - - /** - * The yahoo provider. - */ - private ProtocolProviderServiceYahooImpl yahooProvider; - - private final OperationSetFileTransferYahooImpl fileTransferOpSet; - - private Contact sender; - - private Date date; - - private String fileName; - - private long fileSize; - - public IncomingFileTransferRequestYahooImpl( - ProtocolProviderServiceYahooImpl yahooProvider, - OperationSetFileTransferYahooImpl fileTransferOpSet, - Contact sender, - Date date, - String fileName, - String fileSize, - String id) - { - this.yahooProvider = yahooProvider; - this.fileTransferOpSet = fileTransferOpSet; - this.sender = sender; - this.date = date; - this.fileName = fileName; - - try - { - this.fileSize = Long.valueOf(fileSize); - } - catch (NumberFormatException e) - {} - - this.id = id; - } - - /** - * Unique ID that is identifying the request and then the FileTransfer - * if the request has been accepted. - * - * @return the id. - */ - public String getID() - { - return id; - } - - /** - * Returns a String that represents the name of the file that is being - * received. - * If there is no name, returns null. - * @return a String that represents the name of the file - */ - public String getFileName() - { - return fileName; - } - - /** - * Returns a String that represents the description of the file that is - * being received. - * If there is no description available, returns null. - * - * @return a String that represents the description of the file - */ - public String getFileDescription() - { - return ""; - } - - /** - * Returns a long that represents the size of the file that is being - * received. - * If there is no file size available, returns null. - * - * @return a long that represents the size of the file - */ - public long getFileSize() - { - return fileSize; - } - - /** - * Returns a String that represents the name of the sender of the file - * being received. - * If there is no sender name available, returns null. - * - * @return a String that represents the name of the sender - */ - public Contact getSender() - { - return sender; - } - - /** - * Function called to accept and receive the file. - * - * @param file the file to accept - * @return the <tt>FileTransfer</tt> object managing the transfer - */ - public FileTransfer acceptFile(File file) - { - AbstractFileTransfer incomingTransfer = null; - - incomingTransfer = - new FileTransferImpl(yahooProvider, - id, sender, file, FileTransfer.IN); - - yahooProvider.getYahooSession().fileTransferAccept(id, file); - - FileTransferCreatedEvent event - = new FileTransferCreatedEvent(incomingTransfer, new Date()); - - fileTransferOpSet.fireFileTransferCreated(event); - - incomingTransfer.fireStatusChangeEvent( - FileTransferStatusChangeEvent.PREPARING); - - return incomingTransfer; - } - - /** - * Function called to refuse the file. - */ - public void rejectFile() - { - yahooProvider.getYahooSession().fileTransferReject(id); - - fileTransferOpSet.fireFileTransferRequestRejected( - new FileTransferRequestEvent( - fileTransferOpSet, this, this.getDate())); - } - - /** - * @return the date - */ - public Date getDate() - { - return date; - } - - /** - * Returns the thumbnail contained in this request. - * - * @return the thumbnail contained in this request - */ - public byte[] getThumbnail() - { - return null; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java deleted file mode 100644 index e35a3c1..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import net.java.sip.communicator.service.protocol.*; - -/** - * A simple implementation of the <tt>Message</tt> interface. Right now the - * message only supports test contents and no binary data. - * - * @author Damian Minkov - * @author Lubomir Marinov - */ -public class MessageYahooImpl - extends AbstractMessage -{ - - /** - * Creates an instance of this Message with the specified parameters. - * - * @param content the text content of the message. - * @param contentType a MIME string indicating the content type of the - * <tt>content</tt> String. - * @param contentEncoding a MIME String indicating the content encoding of - * the <tt>content</tt> String. - * @param subject the subject of the message or null for empty. - */ - public MessageYahooImpl(String content, String contentType, - String contentEncoding, String subject) - { - super(content, contentType, contentEncoding, subject); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetAdHocMultiUserChatYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetAdHocMultiUserChatYahooImpl.java deleted file mode 100644 index dd5f73f..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetAdHocMultiUserChatYahooImpl.java +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; -import ymsg.network.*; -import ymsg.network.event.*; - -/** - * A Yahoo implementation of the ad-hoc multi user chat operation set. - * - * @author Rupert Burchardi - * @author Valentin Martinet - * @author Yana Stamcheva - */ -public class OperationSetAdHocMultiUserChatYahooImpl -implements OperationSetAdHocMultiUserChat -{ - private static final Logger logger = - Logger.getLogger(OperationSetAdHocMultiUserChatYahooImpl.class); - - /** - * A list of listeners subscribed for invitations multi user chat events. - */ - private final List<AdHocChatRoomInvitationListener> invitationListeners - = new Vector<AdHocChatRoomInvitationListener>(); - - /** - * A list of listeners subscribed for events indicating rejection of a multi - * user chat invitation sent by us. - */ - private final List<AdHocChatRoomInvitationRejectionListener> - invitationRejectionListeners - = new Vector<AdHocChatRoomInvitationRejectionListener>(); - - /** - * Listeners that will be notified of changes in our status in the room such - * as us being kicked, banned, or granted admin permissions. - */ - private final List<LocalUserAdHocChatRoomPresenceListener> presenceListeners - = new Vector<LocalUserAdHocChatRoomPresenceListener>(); - - /** - * A list of the rooms that are currently open by this account. - */ - private final Hashtable<String, AdHocChatRoomYahooImpl> chatRoomCache - = new Hashtable<String, AdHocChatRoomYahooImpl>(); - - /** - * The currently valid Yahoo protocol provider service implementation. - */ - private final ProtocolProviderServiceYahooImpl yahooProvider; - - /** - * The operation set for the basic instant messaging, provides some message - * format functions. - */ - private final OperationSetBasicInstantMessagingYahooImpl opSetBasic; - - /** - * Instantiates the user operation set with a currently valid instance of - * the Yahoo protocol provider. - * - * @param yahooProvider a currently valid instance of - * ProtocolProviderServiceYahooImpl. - */ - OperationSetAdHocMultiUserChatYahooImpl( - ProtocolProviderServiceYahooImpl yahooProvider) - { - this.yahooProvider = yahooProvider; - - yahooProvider - .addRegistrationStateChangeListener(new RegistrationStateListener()); - - opSetBasic = - (OperationSetBasicInstantMessagingYahooImpl) yahooProvider - .getOperationSet(OperationSetBasicInstantMessaging.class); - } - - /** - * Adds a listener to invitation notifications. - * - * @param listener An invitation listener. - */ - public void addInvitationListener(AdHocChatRoomInvitationListener listener) - { - synchronized (invitationListeners) - { - if (!invitationListeners.contains(listener)) - invitationListeners.add(listener); - } - } - - /** - * Removes a listener that was being notified of changes in our status in a - * room such as us being kicked, banned or dropped. - * - * @param listener the <tt>LocalUserAdHocChatRoomPresenceListener</tt>. - */ - public void removeInvitationListener( - AdHocChatRoomInvitationListener listener) - { - synchronized (invitationListeners) - { - invitationListeners.remove(listener); - } - } - - /** - * Subscribes <tt>listener</tt> so that it would receive events indicating - * rejection of a multi user chat invitation that we've sent earlier. - * - * @param listener the listener that we'll subscribe for invitation - * rejection events. - */ - - public void addInvitationRejectionListener( - AdHocChatRoomInvitationRejectionListener listener) - { - synchronized (invitationRejectionListeners) - { - if (!invitationRejectionListeners.contains(listener)) - invitationRejectionListeners.add(listener); - } - } - - /** - * Removes <tt>listener</tt> from the list of invitation listeners - * registered to receive invitation rejection events. - * - * @param listener the invitation listener to remove. - */ - public void removeInvitationRejectionListener( - AdHocChatRoomInvitationRejectionListener listener) - { - synchronized (invitationRejectionListeners) - { - invitationRejectionListeners.remove(listener); - } - } - - /** - * Adds a listener that will be notified of changes in our status in a chat - * room such as us being kicked, banned or dropped. - * - * @param listener the <tt>LocalUserAdHocChatRoomPresenceListener</tt>. - */ - public void addPresenceListener( - LocalUserAdHocChatRoomPresenceListener listener) - { - synchronized (presenceListeners) - { - if (!presenceListeners.contains(listener)) - presenceListeners.add(listener); - } - } - - /** - * Removes a listener that was being notified of changes in our status in a - * room such as us being kicked, banned or dropped. - * - * @param listener the <tt>LocalUserChatRoomPresenceListener</tt>. - */ - public void removePresenceListener( - LocalUserAdHocChatRoomPresenceListener listener) - { - synchronized (presenceListeners) - { - presenceListeners.remove(listener); - } - } - - /** - * Creates a room with the named <tt>roomName</tt> and according to the - * specified <tt>roomProperties</tt> on the server that this protocol - * provider is currently connected to. Note the roomProperties also contain - * users that we like to invite to the chatRoom, this is required in the - * yahoo protocol. - * - * @param roomName the name of the <tt>AdHocChatRoom</tt> to create. - * @param roomProperties properties specifying how the room should be - * created. - * - * @throws OperationFailedException if the room couldn't be created for some - * reason (e.g. room already exists; user already joined to an - * existent room or user has no permissions to create a chat - * room). - * - * @return ChatRoom the chat room that we've just created. - */ - public AdHocChatRoom createAdHocChatRoom(String roomName, - Map<String, Object> roomProperties) - throws OperationFailedException - { - return createAdHocChatRoom(roomName, (String[]) null, ""); - } - - /** - * Creates an ad-hoc room with the named <tt>adHocRoomName</tt> and in - * including to the specified <tt>contacts</tt>. When the method returns the - * ad-hoc room the local user will have joined it. - * - * @return the ad-hoc room that has been just created - * @param adHocRoomName the name of the room to be created - * @param contacts the list of contacts ID - * @param reason the reason for contacts' invitation - * @throws OperationFailedException if the room couldn't be created for - * some reason - */ - public AdHocChatRoom createAdHocChatRoom( String adHocRoomName, - List<String> contacts, - String reason) - throws OperationFailedException - { - String[] contactsToInvite = new String[contacts.size()]; - for(int i=0; i<contacts.size(); i++) - { - contactsToInvite[i] = contacts.get(i); - } - return createAdHocChatRoom( - adHocRoomName, contactsToInvite, reason); - } - - /** - * Creates an ad-hoc room with the named <tt>adHocRoomName</tt> and in - * including to the specified <tt>contacts</tt>. When the method returns the - * ad-hoc room the local user will have joined it. - * - * @param roomName name of the chatroom - * @param invitedContacts contacts to be invited to this room - * @param reason reason of this invitation - * @return AdHocChatRoom the ad-hoc room that has been just created - * @throws OperationFailedException - */ - private AdHocChatRoom createAdHocChatRoom( - String roomName, - String[] invitedContacts, - String reason) - throws OperationFailedException - { - if (invitedContacts == null) - invitedContacts = new String[0]; - - AdHocChatRoom chatRoom = null; - - try - { - YahooConference conference = - yahooProvider.getYahooSession().createConference( - invitedContacts, // users invited to this conference - reason, // invite message / topic - yahooProvider.getYahooSession().getLoginIdentity()); - - chatRoom = createLocalChatRoomInstance(conference); - } - catch (Exception e) - { - String errorMessage - = "Failed to create chat room with name: " + roomName; - - if (logger.isDebugEnabled()) - logger.debug(errorMessage, e); - throw new OperationFailedException(errorMessage, - OperationFailedException.CHAT_ROOM_NOT_JOINED, e); - } - chatRoom.join(); - return chatRoom; - } - - /** - * Creates a <tt>AdHocChatRoom</tt> instance from the specified Yahoo - * conference. - * - * @param yahooConference The chat room model from the yahoo lib. - * - * @return AdHocChatRoom the chat room that we've just created. - */ - private AdHocChatRoomYahooImpl createLocalChatRoomInstance( - YahooConference yahooConference) - { - synchronized (chatRoomCache) - { - AdHocChatRoomYahooImpl newChatRoom - = new AdHocChatRoomYahooImpl(yahooConference, yahooProvider); - - chatRoomCache.put(yahooConference.getName(), newChatRoom); - - return newChatRoom; - } - } - - /** - * Creates a <tt>AdHocChatRoom</tt> instance (where the inviter is - * represented by inviterID parameter) from the specified Yahoo conference. - * - * @param yahooConference The chat room model from the yahoo lib. - * @param inviterID inviter's Yahoo ID which has to be added as room member - * - * @return AdHocChatRoom the chat room that we've just created. - */ - private AdHocChatRoomYahooImpl createLocalChatRoomInstance( - YahooConference yahooConference, String inviterID) - { - synchronized (chatRoomCache) - { - AdHocChatRoomYahooImpl newChatRoom - = new AdHocChatRoomYahooImpl(yahooConference, yahooProvider); - - OperationSetPersistentPresenceYahooImpl opSetPresence = - (OperationSetPersistentPresenceYahooImpl) yahooProvider - .getOperationSet(OperationSetPersistentPresence.class); - - newChatRoom.addChatRoomParticipant( - opSetPresence.findContactByID(inviterID)); - chatRoomCache.put(yahooConference.getName(), newChatRoom); - - return newChatRoom; - } - } - - /** - * Returns the <tt>AdHocChatRoomYahooImpl</tt> corresponding to the given - * <tt>conference</tt> if such exists, otherwise returns null. - * - * @param conference the <tt>YahooConference</tt>, for which we're searching - * correspondence - * @return the <tt>AdHocChatRoomYahooImpl</tt> corresponding to the given - * <tt>conference</tt> if such exists, otherwise returns null - */ - private AdHocChatRoomYahooImpl getLocalChatRoomInstance( - YahooConference conference) - { - synchronized (chatRoomCache) - { - for (AdHocChatRoomYahooImpl chatRoom : chatRoomCache.values()) - { - if (chatRoom.getYahooConference().equals(conference)) - return chatRoom; - } - } - - return null; - } - - /** - * Informs the sender of an invitation that we decline their invitation. - * - * @param invitation the connection to use for sending the rejection. - * @param rejectReason the reason to reject the given invitation - */ - public void rejectInvitation(AdHocChatRoomInvitation invitation, - String rejectReason) - { - AdHocChatRoomYahooImpl chatRoom = - (AdHocChatRoomYahooImpl) invitation.getTargetAdHocChatRoom(); - - try - { - yahooProvider.getYahooSession().declineConferenceInvite( - chatRoom.getYahooConference(), rejectReason); - } - catch (IOException e) - { - if (logger.isDebugEnabled()) - logger.debug("Failed to reject Invitation: " + e); - } - } - - /** - * Delivers a <tt>AdHocChatRoomInvitationReceivedEvent</tt> to all - * registered <tt>AdHocChatRoomInvitationListener</tt>s. - * - * @param targetChatRoom the room that invitation refers to - * @param inviter the inviter that sent the invitation - * @param reason the reason why the inviter sent the invitation - */ - public void fireInvitationEvent(AdHocChatRoom targetChatRoom, - String inviter, String reason) - { - AdHocChatRoomInvitationYahooImpl invitation = - new AdHocChatRoomInvitationYahooImpl(targetChatRoom, inviter, - reason); - - AdHocChatRoomInvitationReceivedEvent evt = - new AdHocChatRoomInvitationReceivedEvent(this, invitation, - new Date(System.currentTimeMillis())); - - Iterable<AdHocChatRoomInvitationListener> listeners; - synchronized (invitationListeners) - { - listeners - = new ArrayList<AdHocChatRoomInvitationListener>( - invitationListeners); - } - - for (AdHocChatRoomInvitationListener listener : listeners) - listener.invitationReceived(evt); - } - - /** - * Delivers a <tt>AdHocChatRoomInvitationRejectedEvent</tt> to all - * registered <tt>AdHocChatRoomInvitationRejectionListener</tt>s. - * - * @param sourceChatRoom the room that invitation refers to - * @param invitee the name of the invitee that rejected the invitation - * @param reason the reason of the rejection - */ - public void fireInvitationRejectedEvent(AdHocChatRoom sourceChatRoom, - String invitee, String reason) - { - AdHocChatRoomInvitationRejectedEvent evt = - new AdHocChatRoomInvitationRejectedEvent( - this, sourceChatRoom, invitee, - reason, new Date(System.currentTimeMillis())); - - Iterable<AdHocChatRoomInvitationRejectionListener> listeners; - synchronized (invitationRejectionListeners) - { - listeners - = new ArrayList<AdHocChatRoomInvitationRejectionListener>( - invitationRejectionListeners); - } - - for (AdHocChatRoomInvitationRejectionListener listener : listeners) - listener.invitationRejected(evt); - } - - /** - * Delivers a <tt>LocalUserAdHocChatRoomPresenceChangeEvent</tt> to all - * registered <tt>LocalUserAdHocChatRoomPresenceListener</tt>s. - * - * @param chatRoom the <tt>ChatRoom</tt> which has been joined, left, etc. - * @param eventType the type of this event; one of LOCAL_USER_JOINED, - * LOCAL_USER_LEFT, etc. - * @param reason the reason - */ - public void fireLocalUserPresenceEvent(AdHocChatRoom chatRoom, - String eventType, String reason) - { - LocalUserAdHocChatRoomPresenceChangeEvent evt = - new LocalUserAdHocChatRoomPresenceChangeEvent( - this, chatRoom, eventType, - reason); - - Iterable<LocalUserAdHocChatRoomPresenceListener> listeners; - synchronized (presenceListeners) - { - listeners = - new ArrayList<LocalUserAdHocChatRoomPresenceListener>( - presenceListeners); - } - - for (LocalUserAdHocChatRoomPresenceListener listener : listeners) - listener.localUserAdHocPresenceChanged(evt); - } - - /** - * 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. - * @throws UnsupportedEncodingException missing utf-8 in platform we use. - */ - private Message createMessage(byte[] content, String contentType, - String contentEncoding, String subject) - throws UnsupportedEncodingException - { - return new MessageYahooImpl(new String(content, "UTF-8"), contentType, - contentEncoding, subject); - } - - /** - * Creates a message by a given message text. - * - * @param messageText The message text. - * @return the newly created message. - */ - public Message createMessage(String messageText) - { - return new MessageYahooImpl(messageText, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE, - OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING, null); - } - - /** - * Our listener that will tell us when we're registered to yahoo network. - * - */ - private class RegistrationStateListener - implements RegistrationStateChangeListener - { - /** - * The method is called by a ProtocolProvider implementation whenever a - * change in the registration state of the corresponding provider had - * occurred. - * - * @param evt ProviderStatusChangeEvent the event describing the status - * change. - */ - public void registrationStateChanged(RegistrationStateChangeEvent evt) - { - if (evt.getNewState() == RegistrationState.REGISTERED) - { - yahooProvider.getYahooSession().addSessionListener( - new YahooMessageListener()); - } - } - } - - /** - * Our group chat message listener, it extends the SessionAdapter from the - * the Yahoo library. - * - */ - private class YahooMessageListener - extends SessionAdapter - { - - @Override - public void conferenceInviteDeclinedReceived(SessionConferenceEvent ev) - { - if (logger.isDebugEnabled()) - logger.debug("Group Chat invite declined received. " - + ev.toString()); - try - { - AdHocChatRoom chatRoom = getLocalChatRoomInstance(ev.getRoom()); - - fireInvitationRejectedEvent(chatRoom, ev.getFrom(), ev - .getMessage()); - } - catch (Exception e) - { - if (logger.isDebugEnabled()) - logger.debug("Error: " + e); - } - } - - @Override - public void conferenceInviteReceived(SessionConferenceEvent ev) - { - if (logger.isDebugEnabled()) - logger.debug("Conference Invite Received: " + ev.toString()); - - try - { - AdHocChatRoom chatRoom = getLocalChatRoomInstance(ev.getRoom()); - - if (chatRoom == null) - { - chatRoom = - createLocalChatRoomInstance(ev.getRoom(), ev.getFrom()); - - fireInvitationEvent( - chatRoom, ev.getFrom(), ev.getMessage()); - } - - } - catch (Exception e) - { - if (logger.isDebugEnabled()) - logger.debug("Error: " + e); - } - } - - @Override - public void conferenceLogoffReceived(SessionConferenceEvent ev) - { - if (logger.isDebugEnabled()) - logger.debug("Conference Logoff Received: " + ev.toString()); - - try - { - AdHocChatRoomYahooImpl chatRoom - = getLocalChatRoomInstance(ev.getRoom()); - - if (chatRoom != null) - { - Contact participant = - chatRoom.findParticipantForAddress(ev.getFrom()); - - chatRoom.removeChatRoomParticipant(participant); - } - } - catch (Exception e) - { - logger - .debug("Failed to remove a user from the chat room. " + e); - } - } - - @Override - public void conferenceLogonReceived(SessionConferenceEvent ev) - { - if (logger.isDebugEnabled()) - logger.debug("Conference Logon Received: " + ev.toString()); - - try - { - AdHocChatRoomYahooImpl chatRoom - = getLocalChatRoomInstance(ev.getRoom()); - - if (chatRoom != null) - { - OperationSetPersistentPresenceYahooImpl presenceOpSet = - (OperationSetPersistentPresenceYahooImpl) chatRoom - .getParentProvider().getOperationSet( - OperationSetPersistentPresence.class); - - Contact participant = - presenceOpSet.findContactByID(ev.getFrom()); - - chatRoom.addChatRoomParticipant(participant); - } - } - catch (Exception e) - { - if (logger.isDebugEnabled()) - logger.debug("Failed to add a user to the chat room. " + e); - } - } - - @Override - public void conferenceMessageReceived(SessionConferenceEvent ev) - { - if (logger.isDebugEnabled()) - logger.debug("Conference Message Received: " + ev.toString()); - - try - { - String formattedMessage = ev.getMessage(); - if (logger.isDebugEnabled()) - logger.debug("original message received : " + formattedMessage); - - formattedMessage = opSetBasic.decodeMessage(formattedMessage); - if (logger.isDebugEnabled()) - logger.debug("formatted Message : " + formattedMessage); - // As no indications in the protocol is it html or not. No harm - // to set all messages html - doesn't affect the appearance of - // the gui - - Message newMessage = - createMessage( - formattedMessage.getBytes("UTF-8"), - OperationSetBasicInstantMessaging.HTML_MIME_TYPE, - OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING, - null); - - AdHocChatRoomYahooImpl chatRoom = - getLocalChatRoomInstance(ev.getRoom()); - - if (chatRoom != null) - { - Contact member = - chatRoom.findParticipantForAddress(ev.getFrom()); - - AdHocChatRoomMessageReceivedEvent msgReceivedEvent = - new AdHocChatRoomMessageReceivedEvent( - chatRoom, - member, - new Date(), - newMessage, - AdHocChatRoomMessageReceivedEvent - .CONVERSATION_MESSAGE_RECEIVED); - - chatRoom.fireMessageEvent(msgReceivedEvent); - } - } - catch (Exception e) - { - logger - .debug("Error while receiving a multi user chat message: " - + e); - } - - } - - @Override - public void connectionClosed(SessionEvent ev) - { - if (logger.isDebugEnabled()) - logger.debug("Connection Closed: " + ev.toString()); - } - } - - public List<AdHocChatRoom> getAdHocChatRooms() - { - return new ArrayList<AdHocChatRoom>(chatRoomCache.values()); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java deleted file mode 100644 index 84059de..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java +++ /dev/null @@ -1,649 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; -import java.util.*; -import java.util.regex.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; -import ymsg.network.event.*; -import ymsg.support.*; - -/** - * A straightforward implementation of the basic instant messaging operation - * set. - * - * @author Damian Minkov - * @author Symphorien Wanko - * @author Keio Kraaner - */ -public class OperationSetBasicInstantMessagingYahooImpl - extends AbstractOperationSetBasicInstantMessaging - implements OperationSetInstantMessageFiltering -{ - /** - * Logger for this class - */ - private static final Logger logger = - Logger.getLogger(OperationSetBasicInstantMessagingYahooImpl.class); - - /** - * Yahoo has limit of message length. If exceeded - * message is not delivered and no notification is received for that. - */ - private static final int MAX_MESSAGE_LENGTH = 800; // 949 - - /** - * A regexp that is used to escape some chars in messages. - */ - private static final Pattern MESSAGE_CHARS_ESCAPE = Pattern.compile("([.()^&$*|])"); - - /** - * A list of filters registered for message events. - */ - private final List<EventFilter> eventFilters = new ArrayList<EventFilter>(); - - /** - * The provider that created us. - */ - private ProtocolProviderServiceYahooImpl yahooProvider = null; - - /** - * Message decoder allows to convert Yahoo formated messages, which can - * contains some specials characters, to HTML or to plain text. - */ - private final MessageDecoder messageDecoder = new MessageDecoder(); - - /** - * A reference to the persistent presence operation set that we use - * to match incoming messages to <tt>Contact</tt>s and vice versa. - */ - private OperationSetPersistentPresenceYahooImpl opSetPersPresence = null; - private static final Pattern FONT_SIZE_0_PATTERN = Pattern.compile("(<font) (.*) size=\"0\">"); - private static final Pattern FONT_SIZE_INT_PATTERN = Pattern.compile("(<font) (.*) size=\"(\\d+)\">"); - - /** - * Creates an instance of this operation set. - * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt> - * that created us and that we'll use for retrieving the underlying aim - * connection. - */ - OperationSetBasicInstantMessagingYahooImpl( - ProtocolProviderServiceYahooImpl provider) - { - this.yahooProvider = provider; - provider.addRegistrationStateChangeListener( - new RegistrationStateListener()); - } - - /** - * Determines wheter the protocol provider (or the protocol itself) support - * sending and receiving offline messages. Most often this method would - * return true for protocols that support offline messages and false for - * those that don't. It is however possible for a protocol to support these - * messages and yet have a particular account that does not (i.e. feature - * not enabled on the protocol server). In cases like this it is possible - * for this method to return true even when offline messaging is not - * supported, and then have the sendMessage method throw an - * OperationFailedException with code - OFFLINE_MESSAGES_NOT_SUPPORTED. - * - * @return <tt>true</tt> if the protocol supports offline messages and - * <tt>false</tt> otherwise. - */ - public boolean isOfflineMessagingSupported() - { - return true; - } - - /** - * Determines wheter 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) || - contentType.equals(HTML_MIME_TYPE)) - return true; - else - return false; - } - - @Override - public Message createMessage(String content, String contentType, - String encoding, String subject) - { - return new MessageYahooImpl(content, contentType, encoding, subject); - } - - /** - * 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 stack is - * not registered and initialized. - * @throws IllegalArgumentException if <tt>to</tt> is not an - * instance of ContactImpl. - */ - public void sendInstantMessage(Contact to, Message message) - throws IllegalStateException, IllegalArgumentException - { - assertConnected(); - - if( !(to instanceof ContactYahooImpl) ) - throw new IllegalArgumentException( - "The specified contact is not a Yahoo contact." - + to); - - try - { - String toUserID = ((ContactYahooImpl) to).getID(); - - MessageDeliveredEvent msgDeliveryPendingEvt = - new MessageDeliveredEvent(message, to, new Date()); - - MessageDeliveredEvent[] msgDeliveryPendingEvts = messageDeliveryPendingTransform(msgDeliveryPendingEvt); - - if (msgDeliveryPendingEvts == null || msgDeliveryPendingEvts.length == 0) - return; - - for (MessageDeliveredEvent event : msgDeliveryPendingEvts) - { - byte[] msgBytesToBeSent = - event.getSourceMessage().getContent().trim() - .getBytes("UTF-8"); - - // split the message in parts with max allowed length - // and send them all - do - { - if (msgBytesToBeSent.length > MAX_MESSAGE_LENGTH) - { - byte[] tmp1 = new byte[MAX_MESSAGE_LENGTH]; - System.arraycopy(msgBytesToBeSent, 0, tmp1, 0, - MAX_MESSAGE_LENGTH); - - byte[] tmp2 = - new byte[msgBytesToBeSent.length - - MAX_MESSAGE_LENGTH]; - System.arraycopy(msgBytesToBeSent, MAX_MESSAGE_LENGTH, - tmp2, 0, tmp2.length); - - msgBytesToBeSent = tmp2; - - yahooProvider.getYahooSession().sendMessage(toUserID, - new String(tmp1, "UTF-8")); - } - else - { - yahooProvider.getYahooSession().sendMessage(toUserID, - new String(msgBytesToBeSent, "UTF-8")); - } - - MessageDeliveredEvent msgDeliveredEvt = - new MessageDeliveredEvent(message, to, new Date()); - - if (msgDeliveredEvt != null) - fireMessageEvent(msgDeliveredEvt); - } - while (msgBytesToBeSent.length > MAX_MESSAGE_LENGTH); - } - } - catch (IOException ex) - { - logger.fatal("Cannot Send Message! " + ex.getMessage()); - MessageDeliveryFailedEvent evt = - new MessageDeliveryFailedEvent( - message, - to, - MessageDeliveryFailedEvent.NETWORK_FAILURE); - - if (evt != null) - fireMessageEvent(evt); - } - } - - /** - * Utility method throwing an exception if the stack is not properly - * initialized. - * @throws IllegalStateException if the underlying stack is - * not registered and initialized. - */ - private void assertConnected() throws IllegalStateException - { - if (yahooProvider == null) - throw new IllegalStateException( - "The provider must be non-null and signed on the " - +"service before being able to communicate."); - if (!yahooProvider.isRegistered()) - throw new IllegalStateException( - "The provider must be signed on the service before " - +"being able to communicate."); - } - - /** - * Our listener that will tell us when we're registered to - */ - private class RegistrationStateListener - implements RegistrationStateChangeListener - { - /** - * The method is called by a ProtocolProvider implementation whenver - * a change in the registration state of the corresponding provider had - * occurred. - * @param evt ProviderStatusChangeEvent the event describing the status - * change. - */ - public void registrationStateChanged(RegistrationStateChangeEvent evt) - { - if (logger.isDebugEnabled()) - logger.debug("The provider changed state from: " - + evt.getOldState() - + " to: " + evt.getNewState()); - - if (evt.getNewState() == RegistrationState.REGISTERED) - { - opSetPersPresence = - (OperationSetPersistentPresenceYahooImpl) yahooProvider - .getOperationSet(OperationSetPersistentPresence.class); - - yahooProvider.getYahooSession(). - addSessionListener(new YahooMessageListener()); - } - } - } - - /** - * Delivers the specified event to all registered message listeners. - * @param evt the <tt>EventObject</tt> that we'd like delivered to all - * registered message listerners. - */ - @Override - protected void fireMessageEvent(EventObject evt) - { - // check if this event should be filtered out - Iterator<EventFilter> filters; - synchronized (eventFilters) - { - filters = new ArrayList<EventFilter>(eventFilters).iterator(); - } - // return if a filter has filtered this event out - boolean filtered = false; - while (filters.hasNext()) - { - try - { - if (filters.next().filterEvent(evt)) - { - filtered = true; - } - } - catch(Exception exc) - { - logger.error("An exception occurred while filtering an event.", - exc); - } - } - - if (filtered) - { - if (logger.isTraceEnabled()) - logger.trace("Message event filtered."); - return; - } - - super.fireMessageEvent(evt); - } - - /** - * This class provides methods to listen for yahoo events which interest us. - */ - private class YahooMessageListener - extends SessionAdapter - { - /** - * Overrides <tt>messageReceived</tt> from <tt>SessionAdapter</tt>, - * called when we receive a new intant message. - * - * @param ev Event with information on the received message - */ - @Override - public void messageReceived(SessionEvent ev) - { - handleNewMessage(ev); - } - - /** - * Overrides <tt>offlineMessageReceived</tt> from <tt>SessionAdapter</tt>, - * called when we receive a message which has been sent to us - * when we were offline. - * - * @param ev Event with information on the received message - */ - @Override - public void offlineMessageReceived(SessionEvent ev) - { - handleNewMessage(ev); - } - - /** - * Overrides <tt>newMailReceived</tt> from <tt>SessionAdapter</tt>, - * called when yahoo alert us that there is a new message in our mailbox. - * There is two types of notification, the first one provides only - * the number of unread mails and the second gives informations about - * a precise new mail. Here, we care about only the second case in which - * we should always have the email of the sender of the mail. - * - * @param ev Event with information on the received email - */ - @Override - public void newMailReceived(SessionNewMailEvent ev) - { - // why, if I provide mail@yahoo.FR when registering my account, - // SC later tells me that my email address is mail@yahoo.COM ?? - // because of this users will always be sent on yahoo.com mail - // login page rather than their usual (yahoo.XXX) login page. - String myEmail = yahooProvider.getAccountID().getAccountAddress(); - - // we don't process incoming email event without source address. - // it allows us to avoid some spams - if ((ev.getEmailAddress() == null) - || (ev.getEmailAddress().indexOf('@') < 0)) - { - return; - } - - String yahooMailLogon = "http://mail." - + myEmail.substring(myEmail.indexOf('@') + 1); - - yahooMailLogon = " <a href=\"" - + yahooMailLogon + "\">" - + yahooMailLogon + "</a>"; - - // FIXME Escape HTML! - String newMail = YahooActivator.getResources().getI18NString( - "service.gui.NEW_MAIL", - new String[]{ev.getFrom(), - "<" + ev.getEmailAddress() + ">", - ev.getSubject(), - " "+yahooMailLogon}) ; - - Message newMailMessage = new MessageYahooImpl( - newMail, - HTML_MIME_TYPE, - DEFAULT_MIME_ENCODING, - null); - - Contact sourceContact = opSetPersPresence. - findContactByID(ev.getEmailAddress()); - - if (sourceContact == null) - { - if (logger.isDebugEnabled()) - logger.debug("received a new mail from an unknown contact: " - + ev.getFrom() - + " <" + ev.getEmailAddress() + ">"); - //create the volatile contact - sourceContact = opSetPersPresence - .createVolatileContact(ev.getEmailAddress()); - } - MessageReceivedEvent msgReceivedEvt - = new MessageReceivedEvent( - newMailMessage, sourceContact, new Date(), - MessageReceivedEvent.SYSTEM_MESSAGE_RECEIVED); - - fireMessageEvent(msgReceivedEvt); - } - - /** - * Handle incoming message by creating an appropriate Sip Communicator - * <tt>Message</tt> and firing a <tt>MessageReceivedEvent</tt> - * to interested listeners. - * - * @param ev The original <tt>SessionEvent</tt> which noticed us - * of an incoming message. - */ - private void handleNewMessage(SessionEvent ev) - { - if (logger.isDebugEnabled()) - logger.debug("Message received : " + ev); - - // to keep things simple, we can decodeToText() - //String formattedMessage = processLinks( - // messageDecoder.decodeToText(ev.getMessage())); - - String formattedMessage = ev.getMessage(); - if (logger.isDebugEnabled()) - logger.debug("original message received : " + formattedMessage); - - formattedMessage = decodeMessage(formattedMessage); - - if (logger.isDebugEnabled()) - logger.debug("formatted Message : " + formattedMessage); - // As no indications in the protocol is it html or not. No harm - // to set all messages html - doesn't affect the appearance of the - // gui - Message newMessage = - createMessage(formattedMessage, HTML_MIME_TYPE, - DEFAULT_MIME_ENCODING, null); - - Contact sourceContact = opSetPersPresence. - findContactByID(ev.getFrom()); - - if(sourceContact == null) - { - if (logger.isDebugEnabled()) - logger.debug("received a message from an unknown contact: " - + ev.getFrom()); - //create the volatile contact - sourceContact = opSetPersPresence - .createVolatileContact(ev.getFrom()); - } - - MessageReceivedEvent msgReceivedEvt - = new MessageReceivedEvent( - newMessage, sourceContact , new Date()); - - // msgReceivedEvt = messageReceivedTransform(msgReceivedEvt); - - if (msgReceivedEvt != null) - fireMessageEvent(msgReceivedEvt); - } - } - - /** - * Decode the received chat message. - * If the message contains \u001b the following text is decoded by - * the MessageDecoder of yahoo api - * Then make http links clickable and fix the font size of html code - * - * @param message the chat message - * @return a decoded message. - */ - String decodeMessage(String message) - { - message = messageDecoder.decodeToHTML(message); - message = processLinks(message); - message = - FONT_SIZE_0_PATTERN.matcher(message) - .replaceAll("$1 $2 size=\"10\">"); - message = - FONT_SIZE_INT_PATTERN.matcher(message) - .replaceAll("$1 $2 style=\"font-size: $3px;\">"); - return message; - } - - /** - * Format links in the given message. Skips all links, which are already in - * HTML format and converts all other links. - * - * @param message The source message string. - * @return The message string with properly formatted links. - */ - public String processLinks(String message) - { - StringBuffer msgBuffer = new StringBuffer(); - - // We match two groups of Strings. The first group is the group of any - // String. The second group is a well formatted HTML link. - Pattern p = Pattern.compile("(.*?)(<a[\\s][^<]*(/>|</a>))", - Pattern.CASE_INSENSITIVE); - - Matcher m = p.matcher(message); - - int lastMatchIndex = 0; - while (m.find()) - { - lastMatchIndex = m.end(); - - String matchGroup1 = m.group(1); - String matchGroup2 = m.group(2); - - String formattedString = formatLinksToHTML(matchGroup1); - - m.appendReplacement(msgBuffer, - replaceSpecialRegExpChars(formattedString) + matchGroup2); - } - - String tailString = message.substring(lastMatchIndex); - - String formattedTailString = formatLinksToHTML(tailString); - - msgBuffer.append(formattedTailString); - - return msgBuffer.toString(); - } - - /** - * Replaces some chars that are special in a regular expression. - * - * @param text The initial text. - * @return the formatted text - */ - private static String replaceSpecialRegExpChars(String text) - { - return MESSAGE_CHARS_ESCAPE.matcher(text).replaceAll("\\\\$1"); - } - - /** - * Goes through the given text and converts all links to HTML links. - * <p> - * For example all occurrences of http://jitsi.org/ will be - * replaced by <a href="http://jitsi.org/"> - * http://jitsi.org/</a>. The same is true for all strings - * starting with "www". - * - * @param text the text on which the regular expression would be performed - * @return the initial text containing only HTML links - */ - private static String formatLinksToHTML(String text) - { - String wwwURL = "(www\\." + // Matches the "www" string. - "[^/?#<\"'\\s]+" + // Matches at least one char of - // any type except / ? # < " ' - // and space. - "[\\.]" + // Matches the second point of the link. - "[^?#<\"'\\s]+" + // Matches at least one char of - // any type except ? # < " ' - // and space. - "(\\?[^#<\"'\\s]*)?" + - "(#.*)?)"; - - String protocolURL - = "([^\"'<>:/?#\\s]+" + // Matches at least one char of - // any type except " ' < > : / ? # - // and space. - "://" + // Matches the :// delimiter in links - "[^/?#<\"'\\s]*" + // Matches any number of times any char - // except / ? # < " ' and space. - "[^?#<\"'\\s]*" + // Matches any number of times any char - // except ? # < " ' and space. - "(\\?[^#<\"'\\s]*)?" + - "(#.*)?)"; - - String url = '(' + wwwURL + '|' + protocolURL + ')'; - - Pattern p = Pattern.compile(url, Pattern.CASE_INSENSITIVE); - - Matcher m = p.matcher(text); - - StringBuffer linkBuffer = new StringBuffer(); - - while (m.find()) - { - String linkGroup = m.group(); - - String replacement; - if (linkGroup.startsWith("www")) - { - replacement = "<A href=\"" + "http://" - + linkGroup + "\">" + linkGroup + "</A>"; - } - else - { - replacement = "<A href=\"" + linkGroup - + "\">" + linkGroup + "</A>"; - } - - m.appendReplacement(linkBuffer, - replaceSpecialRegExpChars(replacement)); - } - - m.appendTail(linkBuffer); - - return linkBuffer.toString(); - } - - /** - * Registers an <tt>EventFilter</tt> with this operation set so that - * events, that do not need processing, are filtered out. - * - * @param filter the <tt>EventFilter</tt> to register. - */ - public void addEventFilter(EventFilter filter) - { - synchronized(eventFilters) - { - if(!eventFilters.contains(filter)) - { - eventFilters.add(filter); - } - } - } - - /** - * Unregisteres an <tt>EventFilter</tt> so that it won't check any more - * if an event should be filtered out. - * - * @param filter the <tt>EventFilter</tt> to unregister. - */ - public void removeEventFilter(EventFilter filter) - { - synchronized(eventFilters) - { - eventFilters.remove(filter); - } - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetFileTransferYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetFileTransferYahooImpl.java deleted file mode 100644 index 85f25b1..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetFileTransferYahooImpl.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; -import ymsg.network.event.*; - -/** - * The Yahoo protocol filetransfer OperationSet. - * - * @author Damian Minkov - */ -public class OperationSetFileTransferYahooImpl - implements OperationSetFileTransfer, - SessionFileTransferListener -{ - /** - * The logger for this class. - */ - private static final Logger logger = - Logger.getLogger(OperationSetFileTransferYahooImpl.class); - - /** - * The provider that created us. - */ - private final ProtocolProviderServiceYahooImpl yahooProvider; - - /** - * A list of listeners registered for file transfer events. - */ - private ArrayList<FileTransferListener> fileTransferListeners - = new ArrayList<FileTransferListener>(); - - /** - * A list of active fileTransfers. - */ - private Hashtable<String, Object> activeFileTransfers - = new Hashtable<String, Object>(); - - /** - * Constructor - * @param provider is the provider that created us - */ - public OperationSetFileTransferYahooImpl( - ProtocolProviderServiceYahooImpl provider) - { - this.yahooProvider = provider; - - provider.addRegistrationStateChangeListener( - new RegistrationStateListener()); - } - - /** - * Sends a file transfer request to the given <tt>toContact</tt> by - * specifying the local and remote file path and the <tt>fromContact</tt>, - * sending the file. - * - * @param toContact the contact that should receive the file - * @param file the file to send - * - * @return the transfer object - * - * @throws IllegalStateException if the protocol provider is not registered - * or connected - * @throws IllegalArgumentException if some of the arguments doesn't fit the - * protocol requirements - */ - public FileTransfer sendFile( Contact toContact, - File file) - throws IllegalStateException, - IllegalArgumentException - { - try - { - assertConnected(); - - if(file.length() > getMaximumFileLength()) - throw new IllegalArgumentException( - "File length exceeds the allowed one for this protocol"); - - ArrayList<String> filesToSend = new ArrayList<String>(); - filesToSend.add(file.getCanonicalPath()); - Date sentDate = new Date(); - String id = yahooProvider.getYahooSession().sendFiles( - filesToSend, toContact.getAddress()); - - FileTransferImpl ft = - new FileTransferImpl(yahooProvider, - id, toContact, file, FileTransfer.OUT); - - // Notify all interested listeners that a file transfer has been - // created. - FileTransferCreatedEvent event - = new FileTransferCreatedEvent(ft, sentDate); - - fireFileTransferCreated(event); - - ft.fireStatusChangeEvent(FileTransferStatusChangeEvent.PREPARING); - - return ft; - } - catch(IOException e) - { - logger.error("Cannot send fileTransfer", e); - return null; - } - } - - /** - * Sends a file transfer request to the given <tt>toContact</tt> by - * specifying the local and remote file path and the <tt>fromContact</tt>, - * sending the file. - * - * @param toContact the contact that should receive the file - * @param fromContact the contact sending the file - * @param remotePath the remote file path - * @param localPath the local file path - * - * @return the transfer object - * - * @throws IllegalStateException if the protocol provider is not registered - * or connected - * @throws IllegalArgumentException if some of the arguments doesn't fit the - * protocol requirements - */ - public FileTransfer sendFile( Contact toContact, - Contact fromContact, - String remotePath, - String localPath) - throws IllegalStateException, - IllegalArgumentException - { - return this.sendFile(toContact, new File(localPath)); - } - - /** - * Adds the given <tt>FileTransferListener</tt> that would listen for - * file transfer requests and created file transfers. - * - * @param listener the <tt>FileTransferListener</tt> to add - */ - public void addFileTransferListener( - FileTransferListener listener) - { - synchronized(fileTransferListeners) - { - if(!fileTransferListeners.contains(listener)) - { - this.fileTransferListeners.add(listener); - } - } - } - - /** - * Removes the given <tt>FileTransferListener</tt> that listens for - * file transfer requests and created file transfers. - * - * @param listener the <tt>FileTransferListener</tt> to remove - */ - public void removeFileTransferListener( - FileTransferListener listener) - { - synchronized(fileTransferListeners) - { - this.fileTransferListeners.remove(listener); - } - } - - /** - * Utility method throwing an exception if the stack is not properly - * initialized. - * @throws java.lang.IllegalStateException if the underlying stack is - * not registered and initialized. - */ - private void assertConnected() - throws IllegalStateException - { - if (yahooProvider == null) - throw new IllegalStateException( - "The provider must be non-null and signed on the " - +"service before being able to send a file."); - else if (!yahooProvider.isRegistered()) - throw new IllegalStateException( - "The provider must be signed on the service before " - +"being able to send a file."); - } - - /** - * Delivers the file transfer to all registered listeners. - * - * @param event the <tt>FileTransferEvent</tt> that we'd like delivered to - * all registered file transfer listeners. - */ - void fireFileTransferCreated(FileTransferCreatedEvent event) - { - activeFileTransfers.put( - event.getFileTransfer().getID(), event.getFileTransfer()); - - Iterator<FileTransferListener> listeners = null; - synchronized (fileTransferListeners) - { - listeners = new ArrayList<FileTransferListener> - (fileTransferListeners).iterator(); - } - - while (listeners.hasNext()) - { - FileTransferListener listener = listeners.next(); - listener.fileTransferCreated(event); - } - } - - /** - * Delivers the specified event to all registered file transfer listeners. - * - * @param event the <tt>EventObject</tt> that we'd like delivered to all - * registered file transfer listeners. - */ - void fireFileTransferRequestRejected(FileTransferRequestEvent event) - { - Iterator<FileTransferListener> listeners = null; - synchronized (fileTransferListeners) - { - listeners = new ArrayList<FileTransferListener> - (fileTransferListeners).iterator(); - } - - while (listeners.hasNext()) - { - FileTransferListener listener = listeners.next(); - - listener.fileTransferRequestRejected(event); - } - } - - /** - * Delivers the specified event to all registered file transfer listeners. - * - * @param event the <tt>EventObject</tt> that we'd like delivered to all - * registered file transfer listeners. - */ - private void fireFileTransferRequest(FileTransferRequestEvent event) - { - Iterator<FileTransferListener> listeners = null; - synchronized (fileTransferListeners) - { - listeners = new ArrayList<FileTransferListener> - (fileTransferListeners).iterator(); - } - - while (listeners.hasNext()) - { - FileTransferListener listener = listeners.next(); - - listener.fileTransferRequestReceived(event); - } - } - - /** - * Delivers the specified event to all registered file transfer listeners. - * - * @param event the <tt>EventObject</tt> that we'd like delivered to all - * registered file transfer listeners. - */ - void fireFileTransferRequestCanceled(FileTransferRequestEvent event) - { - Iterator<FileTransferListener> listeners = null; - synchronized (fileTransferListeners) - { - listeners = new ArrayList<FileTransferListener> - (fileTransferListeners).iterator(); - } - - while (listeners.hasNext()) - { - FileTransferListener listener = listeners.next(); - - listener.fileTransferRequestCanceled(event); - } - } - - private int getStateMapping(int s) - { - switch(s) - { - case SessionFileTransferEvent.REFUSED : - return FileTransferStatusChangeEvent.REFUSED; - case SessionFileTransferEvent.CANCEL : - return FileTransferStatusChangeEvent.CANCELED; - case SessionFileTransferEvent.FAILED : - return FileTransferStatusChangeEvent.FAILED; - case SessionFileTransferEvent.IN_PROGRESS : - return FileTransferStatusChangeEvent.IN_PROGRESS; - case SessionFileTransferEvent.RECEIVED : - return FileTransferStatusChangeEvent.COMPLETED; - case SessionFileTransferEvent.SENT : - return FileTransferStatusChangeEvent.COMPLETED; - default: return FileTransferStatusChangeEvent.WAITING; - } - } - - /** - * Starting point for incoming filetransfer. - * @param ev - */ - public void fileTransferRequestReceived(SessionFileTransferEvent ev) - { - OperationSetPersistentPresenceYahooImpl opSetPersPresence - = (OperationSetPersistentPresenceYahooImpl) - yahooProvider.getOperationSet( - OperationSetPersistentPresence.class); - - Contact sender = opSetPersPresence.findContactByID(ev.getFrom()); - - if(sender == null) - return; - - Date recvDate = new Date(); - - for(int i = 0; i < ev.getFileNames().size(); i++) - { - String fileName = ev.getFileNames().get(i); - String fileSize = ev.getFileSizes().get(i); - - IncomingFileTransferRequest req = - new IncomingFileTransferRequestYahooImpl( - yahooProvider, this, sender, recvDate, - fileName, fileSize, - ev.getId()); - - activeFileTransfers.put(ev.getId(), req); - fireFileTransferRequest( - new FileTransferRequestEvent(this, req, recvDate)); - } - } - - /** - * Status changed for filetransfer. - * @param ev - */ - public void statusChanged(SessionFileTransferEvent ev) - { - if(ev.getId() == null) - return; - - Object ftObj = activeFileTransfers.get(ev.getId()); - - if(ftObj == null) - { - logger.warn("File Transfer or request not found. " + ev.getId() + "/ " + ev.getState()); - return; - } - - int newState = ev.getState(); - - if(newState == SessionFileTransferEvent.CANCEL - || newState == SessionFileTransferEvent.FAILED - || newState == SessionFileTransferEvent.RECEIVED - || newState == SessionFileTransferEvent.REFUSED - || newState == SessionFileTransferEvent.SENT) - { - // this is an final state so remove it from active filetransfers - activeFileTransfers.remove(ev.getId()); - } - - if(ftObj instanceof IncomingFileTransferRequest) - { - if(newState == SessionFileTransferEvent.REFUSED) - { - IncomingFileTransferRequestYahooImpl req = - (IncomingFileTransferRequestYahooImpl)ftObj; - fireFileTransferRequestCanceled( - new FileTransferRequestEvent(this, req, req.getDate())); - return; - } - } - - if(!(ftObj instanceof FileTransferImpl)) - { - logger.warn("File Transfer not found." + ftObj); - return; - } - - FileTransferImpl ft = (FileTransferImpl)ftObj; - - if( newState == SessionFileTransferEvent.IN_PROGRESS) - { - // if we start sending progress fire that we are in progress - if(ev.getProgress() == 0) - ft.fireStatusChangeEvent( - FileTransferStatusChangeEvent.IN_PROGRESS); - - ft.setTransferedBytes(ev.getProgress()); - ft.fireProgressChangeEvent( - System.currentTimeMillis(), ev.getProgress()); - } - else - ft.fireStatusChangeEvent(getStateMapping(newState)); - } - - /** - * Returns the maximum file length supported by the protocol in bytes. - * Supports up to 256MB. - * - * @return the file length that is supported. - */ - public long getMaximumFileLength() - { - return 268435456l;// = 256*1024*1024; - } - - /** - * Our listener that will tell us when we're registered to - */ - private class RegistrationStateListener - implements RegistrationStateChangeListener - { - /** - * The method is called by a ProtocolProvider implementation whenever - * a change in the registration state of the corresponding provider had - * occurred. - * @param evt ProviderStatusChangeEvent the event describing the status - * change. - */ - public void registrationStateChanged(RegistrationStateChangeEvent evt) - { - if (logger.isDebugEnabled()) - logger.debug("The provider changed state from: " - + evt.getOldState() - + " to: " + evt.getNewState()); - - if (evt.getNewState() == RegistrationState.REGISTERED) - { - yahooProvider.getYahooSession().addSessionFileListener( - OperationSetFileTransferYahooImpl.this); - } - else if (evt.getNewState() == RegistrationState.UNREGISTERED) - { - YahooSession ys = yahooProvider.getYahooSession(); - if(ys != null) - ys.removeSessionFileListener( - OperationSetFileTransferYahooImpl.this); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java deleted file mode 100644 index a99bdb7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java +++ /dev/null @@ -1,954 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.service.protocol.yahooconstants.*; -import net.java.sip.communicator.util.*; -import ymsg.network.*; -import ymsg.network.event.*; - -/** - * The Yahoo implementation of a Persistent Presence Operation set. This class - * manages our own presence status as well as subscriptions for the presence - * status of our buddies. It also offers methods for retrieving and modifying - * the buddy contact list and adding listeners for changes in its layout. - * - * @author Damian Minkov - */ -public class OperationSetPersistentPresenceYahooImpl - extends AbstractOperationSetPersistentPresence<ProtocolProviderServiceYahooImpl> -{ - private static final Logger logger = - Logger.getLogger(OperationSetPersistentPresenceYahooImpl.class); - - /** - * Contains our current status message. Note that this field would only - * be changed once the server has confirmed the new status message and - * not immediately upon setting a new one.. - */ - private String currentStatusMessage = ""; - - /** - * The presence status that we were last notified of entering. - * The initial one is OFFLINE - */ - private PresenceStatus currentStatus = YahooStatusEnum.OFFLINE; - - /** - * Sometimes status changes are received before the contact list is inited - * here we store such events so we can show them correctly - */ -// private Hashtable earlyStatusChange = new Hashtable(); - - /** - * The array list we use when returning from the getSupportedStatusSet() - * method. - */ - private static final List<PresenceStatus> supportedPresenceStatusSet = new ArrayList<PresenceStatus>(); - static{ - supportedPresenceStatusSet.add(YahooStatusEnum.AVAILABLE); - supportedPresenceStatusSet.add(YahooStatusEnum.BE_RIGHT_BACK); - supportedPresenceStatusSet.add(YahooStatusEnum.BUSY); - supportedPresenceStatusSet.add(YahooStatusEnum.IDLE); - supportedPresenceStatusSet.add(YahooStatusEnum.INVISIBLE); - supportedPresenceStatusSet.add(YahooStatusEnum.NOT_AT_DESK); - supportedPresenceStatusSet.add(YahooStatusEnum.NOT_AT_HOME); - supportedPresenceStatusSet.add(YahooStatusEnum.NOT_IN_OFFICE); - supportedPresenceStatusSet.add(YahooStatusEnum.OFFLINE); - supportedPresenceStatusSet.add(YahooStatusEnum.ON_THE_PHONE); - supportedPresenceStatusSet.add(YahooStatusEnum.ON_VACATION); - supportedPresenceStatusSet.add(YahooStatusEnum.OUT_TO_LUNCH); - supportedPresenceStatusSet.add(YahooStatusEnum.STEPPED_OUT); - } - - /** - * A map containing bindings between SIP Communicator's yahoo presence status - * instances and Yahoo status codes - */ - private static final Map<PresenceStatus, Long> scToYahooModesMappings - = new Hashtable<PresenceStatus, Long>(); - static{ - scToYahooModesMappings.put(YahooStatusEnum.AVAILABLE, - StatusConstants.STATUS_AVAILABLE); - scToYahooModesMappings.put(YahooStatusEnum.BE_RIGHT_BACK, - StatusConstants.STATUS_BRB); - scToYahooModesMappings.put(YahooStatusEnum.BUSY, - StatusConstants.STATUS_BUSY); - scToYahooModesMappings.put(YahooStatusEnum.IDLE, - StatusConstants.STATUS_IDLE); - scToYahooModesMappings.put(YahooStatusEnum.INVISIBLE, - StatusConstants.STATUS_INVISIBLE); - scToYahooModesMappings.put(YahooStatusEnum.NOT_AT_DESK, - StatusConstants.STATUS_NOTATDESK); - scToYahooModesMappings.put(YahooStatusEnum.NOT_AT_HOME, - StatusConstants.STATUS_NOTATHOME); - scToYahooModesMappings.put(YahooStatusEnum.NOT_IN_OFFICE, - StatusConstants.STATUS_NOTINOFFICE); - scToYahooModesMappings.put(YahooStatusEnum.OFFLINE, - StatusConstants.STATUS_OFFLINE); - scToYahooModesMappings.put(YahooStatusEnum.ON_THE_PHONE, - StatusConstants.STATUS_ONPHONE); - scToYahooModesMappings.put(YahooStatusEnum.ON_VACATION, - StatusConstants.STATUS_ONVACATION); - scToYahooModesMappings.put(YahooStatusEnum.OUT_TO_LUNCH, - StatusConstants.STATUS_OUTTOLUNCH); - scToYahooModesMappings.put(YahooStatusEnum.STEPPED_OUT, - StatusConstants.STATUS_STEPPEDOUT); - } - - /** - * The server stored contact list that will be encapsulating smack's - * buddy list. - */ - private ServerStoredContactListYahooImpl ssContactList = null; - - /** - * Listens for events that are fired while registering to server. - * After we are registered instance is cleared and never used. - */ - private EarlyEventListener earlyEventListener = null; - - /** - * Status events are received before subscription one. - * And when subscription is received we deliver - * and the status events. - */ - private StatusUpdater statusUpdater = new StatusUpdater(); - - public OperationSetPersistentPresenceYahooImpl( - ProtocolProviderServiceYahooImpl provider) - { - super(provider); - - ssContactList = new ServerStoredContactListYahooImpl( this , provider); - - parentProvider.addRegistrationStateChangeListener( - new RegistrationStateListener()); - } - - /** - * Registers a listener that would receive events upong changes in server - * stored groups. - * - * @param listener a ServerStoredGroupChangeListener impl that would - * receive events upong group changes. - */ - @Override - public void addServerStoredGroupChangeListener(ServerStoredGroupListener - listener) - { - ssContactList.addGroupListener(listener); - } - - /** - * Creates a group with the specified name and parent in the server - * stored contact list. - * - * @param parent the group where the new group should be created - * @param groupName the name of the new group to create. - * @throws OperationFailedException if such group already exists - */ - public void createServerStoredContactGroup(ContactGroup parent, - String groupName) - throws OperationFailedException - { - assertConnected(); - - if (!parent.canContainSubgroups()) - throw new IllegalArgumentException( - "The specified contact group cannot contain child groups. Group:" - + parent ); - - ssContactList.createGroup(groupName); - } - - /** - * Creates a non persistent contact for the specified address. This would - * also create (if necessary) a group for volatile contacts that would not - * be added to the server stored contact list. The volatile contact would - * remain in the list until it is really added to the contact list or - * until the application is terminated. - * @param id the address of the contact to create. - * @return the newly created volatile <tt>ContactImpl</tt> - */ - public ContactYahooImpl createVolatileContact(String id) - { - return ssContactList.createVolatileContact(id); - } - - /** - * Creates and returns a unresolved contact from the specified - * <tt>address</tt> and <tt>persistentData</tt>. - * - * @param address an identifier of the contact that we'll be creating. - * @param persistentData a String returned Contact's getPersistentData() - * method during a previous run and that has been persistently stored - * locally. - * @param parentGroup the group where the unresolved contact is supposed - * to belong to. - * @return the unresolved <tt>Contact</tt> created from the specified - * <tt>address</tt> and <tt>persistentData</tt> - */ - public Contact createUnresolvedContact(String address, - String persistentData, - ContactGroup parentGroup) - { - if(! (parentGroup instanceof ContactGroupYahooImpl || - parentGroup instanceof RootContactGroupYahooImpl) ) - throw new IllegalArgumentException( - "Argument is not an yahoo contact group (group=" - + parentGroup + ")"); - - ContactYahooImpl contact = - ssContactList.createUnresolvedContact(parentGroup, address); - - contact.setPersistentData(persistentData); - - return contact; - } - - /** - * Creates and returns a unresolved contact from the specified - * <tt>address</tt> and <tt>persistentData</tt>. - * - * @param address an identifier of the contact that we'll be creating. - * @param persistentData a String returned Contact's getPersistentData() - * method during a previous run and that has been persistently stored - * locally. - * @return the unresolved <tt>Contact</tt> created from the specified - * <tt>address</tt> and <tt>persistentData</tt> - */ - public Contact createUnresolvedContact(String address, - String persistentData) - { - return createUnresolvedContact( address - , persistentData - , getServerStoredContactListRoot()); - } - - /** - * Creates and returns a unresolved contact group from the specified - * <tt>address</tt> and <tt>persistentData</tt>. - * - * @param groupUID an identifier, returned by ContactGroup's - * getGroupUID, that the protocol provider may use in order to create - * the group. - * @param persistentData a String returned ContactGroups's - * getPersistentData() method during a previous run and that has been - * persistently stored locally. - * @param parentGroup the group under which the new group is to be - * created or null if this is group directly underneath the root. - * @return the unresolved <tt>ContactGroup</tt> created from the - * specified <tt>uid</tt> and <tt>persistentData</tt> - */ - public ContactGroup createUnresolvedContactGroup(String groupUID, - String persistentData, ContactGroup parentGroup) - { - return ssContactList.createUnresolvedContactGroup(groupUID); - } - - /** - * Returns a reference to the contact with the specified ID in case we - * have a subscription for it and null otherwise/ - * - * @param contactID a String identifier of the contact which we're - * seeking a reference of. - * @return a reference to the Contact with the specified - * <tt>contactID</tt> or null if we don't have a subscription for the - * that identifier. - */ - public Contact findContactByID(String contactID) - { - return ssContactList.findContactById(contactID); - } - - /** - * Returns the status message that was confirmed by the serfver - * - * @return the last status message that we have requested and the aim - * server has confirmed. - */ - public String getCurrentStatusMessage() - { - return currentStatusMessage; - } - - /** - * Returns the protocol specific contact instance representing the local - * user. - * - * @return the Contact (address, phone number, or uin) that the Provider - * implementation is communicating on behalf of. - */ - public Contact getLocalContact() - { - return null; - } - - /** - * Returns a PresenceStatus instance representing the state this provider - * is currently in. - * - * @return the PresenceStatus last published by this provider. - */ - public PresenceStatus getPresenceStatus() - { - return currentStatus; - } - - /** - * Returns the root group of the server stored contact list. - * - * @return the root ContactGroup for the ContactList stored by this - * service. - */ - public ContactGroup getServerStoredContactListRoot() - { - return ssContactList.getRootGroup(); - } - - /** - * Returns the set of PresenceStatus objects that a user of this service - * may request the provider to enter. - * - * @return Iterator a PresenceStatus array containing "enterable" status - * instances. - */ - public Iterator<PresenceStatus> getSupportedStatusSet() - { - return supportedPresenceStatusSet.iterator(); - } - - /** - * Removes the specified contact from its current parent and places it - * under <tt>newParent</tt>. - * - * @param contactToMove the <tt>Contact</tt> to move - * @param newParent the <tt>ContactGroup</tt> where <tt>Contact</tt> - * would be placed. - */ - public void moveContactToGroup(Contact contactToMove, - ContactGroup newParent) - { - assertConnected(); - - if( !(contactToMove instanceof ContactYahooImpl) ) - throw new IllegalArgumentException( - "The specified contact is not an yahoo contact." + contactToMove); - if( !(newParent instanceof ContactGroupYahooImpl) ) - throw new IllegalArgumentException( - "The specified group is not an yahoo contact group." - + newParent); - - ssContactList.moveContact((ContactYahooImpl)contactToMove, - (ContactGroupYahooImpl)newParent); - } - - /** - * Requests the provider to enter into a status corresponding to the - * specified paramters. - * - * @param status the PresenceStatus as returned by - * getRequestableStatusSet - * @param statusMessage the message that should be set as the reason to - * enter that status - * @throws IllegalArgumentException if the status requested is not a - * valid PresenceStatus supported by this provider. - * @throws IllegalStateException if the provider is not currently - * registered. - * @throws OperationFailedException with code NETWORK_FAILURE if - * publishing the status fails due to a network error. - */ - public void publishPresenceStatus(PresenceStatus status, - String statusMessage) throws - IllegalArgumentException, IllegalStateException, - OperationFailedException - { - assertConnected(); - - if (!(status instanceof YahooStatusEnum)) - throw new IllegalArgumentException( - status + " is not a valid Yahoo status"); - - if(status.equals(YahooStatusEnum.OFFLINE)) - { - parentProvider.unregister(); - return; - } - - try - { - if(statusMessage != null && statusMessage.length() != 0) - { - boolean isAvailable = false; - - if(status.equals(YahooStatusEnum.AVAILABLE)) - isAvailable = true; - - // false - away - // true - available - parentProvider.getYahooSession(). - setStatus(statusMessage, isAvailable); - } - - parentProvider.getYahooSession().setStatus( - scToYahooModesMappings.get(status).longValue()); - - fireProviderStatusChangeEvent(currentStatus, status); - } - catch(IOException ex) - { - throw new OperationFailedException("Failed to set Status", - OperationFailedException.NETWORK_FAILURE); - } - } - - /** - * Get the PresenceStatus for a particular contact. - * - * @param contactIdentifier the identifier of the contact whose status - * we're interested in. - * @return PresenceStatus the <tt>PresenceStatus</tt> of the specified - * <tt>contact</tt> - * @throws IllegalArgumentException if <tt>contact</tt> is not a contact - * known to the underlying protocol provider - * @throws IllegalStateException if the underlying protocol provider is - * not registered/signed on a public service. - * @throws OperationFailedException with code NETWORK_FAILURE if - * retrieving the status fails due to errors experienced during - * network communication - */ - public PresenceStatus queryContactStatus(String contactIdentifier) throws - IllegalArgumentException, IllegalStateException, - OperationFailedException - { - - ContactYahooImpl contact = ssContactList.findContactById(contactIdentifier); - if(contact == null) - { - if (logger.isInfoEnabled()) - logger.info("Contact not found id :" + contactIdentifier); - return null; - } - else - return yahooStatusToPresenceStatus(contact.getSourceContact().getStatus()); - } - - /** - * Removes the specified group from the server stored contact list. - * - * @param group the group to remove. - */ - public void removeServerStoredContactGroup(ContactGroup group) - { - assertConnected(); - - if( !(group instanceof ContactGroupYahooImpl) ) - throw new IllegalArgumentException( - "The specified group is not an yahoo contact group: " + group); - - ssContactList.removeGroup(((ContactGroupYahooImpl)group)); - } - - /** - * Removes the specified group change listener so that it won't receive - * any further events. - * - * @param listener the ServerStoredGroupChangeListener to remove - */ - @Override - public void removeServerStoredGroupChangeListener(ServerStoredGroupListener - listener) - { - ssContactList.removeGroupListener(listener); - } - - /** - * Renames the specified group from the server stored contact list. - * - * @param group the group to rename. - * @param newName the new name of the group. - */ - public void renameServerStoredContactGroup(ContactGroup group, - String newName) - { - assertConnected(); - - if( !(group instanceof ContactGroupYahooImpl) ) - throw new IllegalArgumentException( - "The specified group is not an yahoo contact group: " + group); - - throw new UnsupportedOperationException("Renaming group not supported!"); - //ssContactList.renameGroup((ContactGroupYahooImpl)group, newName); - } - - /** - * Handler for incoming authorization requests. - * - * @param handler an instance of an AuthorizationHandler for - * authorization requests coming from other users requesting - * permission add us to their contact list. - */ - public void setAuthorizationHandler(AuthorizationHandler handler) - { - ssContactList.setAuthorizationHandler(handler); - - // we got a handler. Lets process if something has came - // during login process - if(earlyEventListener != null) - { - earlyEventListener.processEarlyAuthorizations(); - earlyEventListener = null; - } - } - - /** - * Persistently adds a subscription for the presence status of the - * contact corresponding to the specified contactIdentifier and indicates - * that it should be added to the specified group of the server stored - * contact list. - * - * @param parent the parent group of the server stored contact list - * where the contact should be added. <p> - * @param contactIdentifier the contact whose status updates we are - * subscribing for. - * @throws IllegalArgumentException if <tt>contact</tt> or - * <tt>parent</tt> are not a contact known to the underlying protocol - * provider. - * @throws IllegalStateException if the underlying protocol provider is - * not registered/signed on a public service. - * @throws OperationFailedException with code NETWORK_FAILURE if - * subscribing fails due to errors experienced during network - * communication - */ - public void subscribe(ContactGroup parent, String contactIdentifier) throws - IllegalArgumentException, IllegalStateException, - OperationFailedException - { - assertConnected(); - - if(! (parent instanceof ContactGroupYahooImpl) ) - throw new IllegalArgumentException( - "Argument is not an yahoo contact group (group=" + parent + ")"); - - ssContactList.addContact((ContactGroupYahooImpl)parent, contactIdentifier); - } - - /** - * Adds a subscription for the presence status of the contact - * corresponding to the specified contactIdentifier. - * - * @param contactIdentifier the identifier of the contact whose status - * updates we are subscribing for. <p> - * @throws IllegalArgumentException if <tt>contact</tt> is not a contact - * known to the underlying protocol provider - * @throws IllegalStateException if the underlying protocol provider is - * not registered/signed on a public service. - * @throws OperationFailedException with code NETWORK_FAILURE if - * subscribing fails due to errors experienced during network - * communication - */ - public void subscribe(String contactIdentifier) throws - IllegalArgumentException, IllegalStateException, - OperationFailedException - { - assertConnected(); - - ssContactList.addContact(contactIdentifier); - } - - /** - * Removes a subscription for the presence status of the specified - * contact. - * - * @param contact the contact whose status updates we are unsubscribing - * from. - * @throws IllegalArgumentException if <tt>contact</tt> is not a contact - * known to the underlying protocol provider - * @throws IllegalStateException if the underlying protocol provider is - * not registered/signed on a public service. - * @throws OperationFailedException with code NETWORK_FAILURE if - * unsubscribing fails due to errors experienced during network - * communication - */ - public void unsubscribe(Contact contact) throws IllegalArgumentException, - IllegalStateException, OperationFailedException - { - assertConnected(); - - if(! (contact instanceof ContactYahooImpl) ) - throw new IllegalArgumentException( - "Argument is not an yahoo contact (contact=" + contact + ")"); - - ssContactList.removeContact((ContactYahooImpl)contact); - } - - /** - * Converts the specified yahoo status to one of the status fields of the - * YahooStatusEnum class. - * - * @param status the yahoo Status - * @return a PresenceStatus instance representation of the yahoo Status - * parameter. The returned result is one of the YahooStatusEnum fields. - */ - YahooStatusEnum yahooStatusToPresenceStatus(long status) - { - if(status == StatusConstants.STATUS_AVAILABLE) - return YahooStatusEnum.AVAILABLE; - else if(status == StatusConstants.STATUS_BRB) - return YahooStatusEnum.BE_RIGHT_BACK; - else if(status == StatusConstants.STATUS_BUSY) - return YahooStatusEnum.BUSY; - else if(status == StatusConstants.STATUS_NOTATHOME) - return YahooStatusEnum.NOT_AT_HOME; - else if(status == StatusConstants.STATUS_NOTATDESK) - return YahooStatusEnum.NOT_AT_DESK; - else if(status == StatusConstants.STATUS_NOTINOFFICE) - return YahooStatusEnum.NOT_IN_OFFICE; - else if(status == StatusConstants.STATUS_ONPHONE) - return YahooStatusEnum.ON_THE_PHONE; - else if(status == StatusConstants.STATUS_ONVACATION) - return YahooStatusEnum.ON_VACATION; - else if(status == StatusConstants.STATUS_OUTTOLUNCH) - return YahooStatusEnum.OUT_TO_LUNCH; - else if(status == StatusConstants.STATUS_STEPPEDOUT) - return YahooStatusEnum.STEPPED_OUT; - else if(status == StatusConstants.STATUS_INVISIBLE) - return YahooStatusEnum.INVISIBLE; - else if(status == StatusConstants.STATUS_IDLE) - return YahooStatusEnum.IDLE; - else if(status == StatusConstants.STATUS_OFFLINE) - return YahooStatusEnum.OFFLINE; - // Yahoo supports custom statuses so if such is set just return available - else - return YahooStatusEnum.AVAILABLE; - } - - /** - * Utility method throwing an exception if the stack is not properly - * initialized. - * @throws java.lang.IllegalStateException if the underlying stack is - * not registered and initialized. - */ - private void assertConnected() throws IllegalStateException - { - if (parentProvider == null) - throw new IllegalStateException( - "The provider must be non-null and signed on the yahoo " - +"service before being able to communicate."); - if (!parentProvider.isRegistered()) - throw new IllegalStateException( - "The provider must be signed on the yahoo service before " - +"being able to communicate."); - } - - /** - * Notify all provider presence listeners of the corresponding event change - * - * @param oldStatus - * the status our stack had so far - * @param newStatus - * the status we have from now on - */ - @Override - protected void fireProviderStatusChangeEvent( - PresenceStatus oldStatus, - PresenceStatus newStatus) - { - if (!oldStatus.equals(newStatus)) - { - currentStatus = newStatus; - - super.fireProviderStatusChangeEvent(oldStatus, newStatus); - } - } - - /** - * Statuses have been received durring login process - * so we will init them once we are logged in - */ - private void initContactStatuses() - { - YahooGroup[] groups = parentProvider.getYahooSession().getGroups(); - - for (YahooGroup item : groups) - { - @SuppressWarnings("unchecked") - Iterable<YahooUser> members = item.getMembers(); - - for (YahooUser user : members) - { - ContactYahooImpl sourceContact = - ssContactList.findContactById(user.getId()); - - if(sourceContact != null) - handleContactStatusChange(sourceContact, user); - } - } - } - - /** - * Our listener that will tell us when we're registered to server - * and is ready to accept us as a listener. - */ - private class RegistrationStateListener - implements RegistrationStateChangeListener - { - /** - * The method is called by a ProtocolProvider implementation whenever - * a change in the registration state of the corresponding provider had - * occurred. - * @param evt ProviderStatusChangeEvent the event describing the status - * change. - */ - public void registrationStateChanged(RegistrationStateChangeEvent evt) - { - if (logger.isDebugEnabled()) - logger.debug("The yahoo provider changed state from: " - + evt.getOldState() - + " to: " + evt.getNewState()); - - if(evt.getNewState() == RegistrationState.REGISTERING) - { - // add new listener waiting for events during login process - earlyEventListener - = new EarlyEventListener(parentProvider.getYahooSession()); - } - else if(evt.getNewState() == RegistrationState.REGISTERED) - { - parentProvider.getYahooSession(). - addSessionListener(new StatusChangedListener()); - - ssContactList.setYahooSession(parentProvider.getYahooSession()); - - initContactStatuses(); - - addSubscriptionListener(statusUpdater); - - if(earlyEventListener != null) - { - earlyEventListener.dispose(); - earlyEventListener = null; - } - } - else if(evt.getNewState() == RegistrationState.UNREGISTERED - || evt.getNewState() == RegistrationState.AUTHENTICATION_FAILED - || evt.getNewState() == RegistrationState.CONNECTION_FAILED) - { - //since we are disconnected, we won't receive any further status - //updates so we need to change by ourselves our own status as - //well as set to offline all contacts in our contact list that - //were online - PresenceStatus oldStatus = currentStatus; - currentStatus = YahooStatusEnum.OFFLINE; - - fireProviderStatusChangeEvent(oldStatus, currentStatus); - - removeSubscriptionListener(statusUpdater); - - //send event notifications saying that all our buddies are - //offline. The protocol does not implement top level buddies - //nor subgroups for top level groups so a simple nested loop - //would be enough. - Iterator<ContactGroup> groupsIter = - getServerStoredContactListRoot().subgroups(); - while(groupsIter.hasNext()) - { - ContactGroup group = groupsIter.next(); - Iterator<Contact> contactsIter = group.contacts(); - - while(contactsIter.hasNext()) - { - ContactYahooImpl contact - = (ContactYahooImpl)contactsIter.next(); - - PresenceStatus oldContactStatus - = contact.getPresenceStatus(); - - if(!oldContactStatus.isOnline()) - continue; - - contact.updatePresenceStatus(YahooStatusEnum.OFFLINE); - - fireContactPresenceStatusChangeEvent( - contact - , contact.getParentContactGroup() - , oldContactStatus, YahooStatusEnum.OFFLINE); - } - } - - // clear listener - if(earlyEventListener != null) - { - earlyEventListener.dispose(); - earlyEventListener = null; - } - } - } - } - - private void handleContactStatusChange(YahooUser yFriend) - { - ContactYahooImpl sourceContact = - ssContactList.findContactById(yFriend.getId()); - - if(sourceContact == null) - { - if(parentProvider.getAccountID().getUserID(). - equals(yFriend.getId())) - { - // thats my own status - if (logger.isTraceEnabled()) - logger.trace("Own status changed to " + yFriend.getStatus()); - PresenceStatus oldStatus = currentStatus; - currentStatus = - yahooStatusToPresenceStatus(yFriend.getStatus()); - fireProviderStatusChangeEvent(oldStatus, currentStatus); - - return; - } - // strange - else - return; - } - - handleContactStatusChange(sourceContact, yFriend); - } - - void handleContactStatusChange(ContactYahooImpl sourceContact, YahooUser yFriend) - { - PresenceStatus oldStatus - = sourceContact.getPresenceStatus(); - - PresenceStatus newStatus = yahooStatusToPresenceStatus(yFriend.getStatus()); - - // statuses maybe the same and only change in status message - sourceContact.setStatusMessage(yFriend.getCustomStatusMessage()); - - // when old and new status are the same do nothing - no change - if(oldStatus.equals(newStatus)) - { - if (logger.isDebugEnabled()) - logger.debug("old(" + oldStatus + ") and new("+ newStatus + ") statuses are the same!"); - return; - } - - sourceContact.updatePresenceStatus(newStatus); - - ContactGroup parent - = ssContactList.findContactGroup(sourceContact); - - if (logger.isDebugEnabled()) - logger.debug("Will Dispatch the contact status event."); - fireContactPresenceStatusChangeEvent(sourceContact, parent, - oldStatus, newStatus); - } - - private class StatusChangedListener - extends SessionAdapter - { - @Override - public void friendsUpdateReceived(SessionFriendEvent evt) - { - if (logger.isDebugEnabled()) - logger.debug("Received a status update for contact " + evt); - - if(evt.getFriend() != null) - { - handleContactStatusChange(evt.getFriend()); - } - else if(evt.getFriends() != null) - { - YahooUser[] yfs = evt.getFriends(); - for (int i = 0; i < yfs.length; i++) - handleContactStatusChange(yfs[i]); - } - } - } - - /** - * Updates the statuses of newly created persistent contacts - */ - private class StatusUpdater - extends SubscriptionAdapter - { - @Override - public void subscriptionCreated(SubscriptionEvent evt) - { - ContactYahooImpl contact = - (ContactYahooImpl)evt.getSourceContact(); - - if(!contact.isPersistent() || !contact.isResolved()) - return; - - handleContactStatusChange(contact, contact.getSourceContact()); - } - } - - private class EarlyEventListener - extends SessionAdapter - { - private final List<SessionAuthorizationEvent> receivedAuthorizations - = new Vector<SessionAuthorizationEvent>(); - - /** - * The <code>YahooSession</code> this instance is listening to because - * the <code>YahooSession</code> isn't available in - * <code>parentProvider</code> after - * {@link RegistrationState#UNREGISTERED} and then this listener cannot - * be removed. - */ - private final YahooSession yahooSession; - - public EarlyEventListener(YahooSession yahooSession) - { - this.yahooSession = yahooSession; - this.yahooSession.addSessionListener(this); - } - - @Override - public void authorizationReceived(SessionAuthorizationEvent ev) - { - if(ev.isAuthorizationRequest()) - { - if (logger.isTraceEnabled()) - logger.trace("authorizationRequestReceived from " + - ev.getFrom()); - receivedAuthorizations.add(ev); - } - } - - public void dispose() - { - yahooSession.removeSessionListener(this); - } - - public void processEarlyAuthorizations() - { - for (SessionAuthorizationEvent e : receivedAuthorizations) - { - ssContactList.processAuthorizationRequest(e); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java deleted file mode 100644 index 81e266a..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; -import ymsg.network.event.*; - -/** - * Maps SIP Communicator typing notifications to those going and coming from - * smack lib. - * - * @author Damian Minkov - */ -public class OperationSetTypingNotificationsYahooImpl - extends AbstractOperationSetTypingNotifications<ProtocolProviderServiceYahooImpl> -{ - private static final Logger logger = - Logger.getLogger(OperationSetTypingNotificationsYahooImpl.class); - - /** - * An active instance of the opSetPersPresence operation set. We're using - * it to map incoming events to contacts in our contact list. - */ - private OperationSetPersistentPresenceYahooImpl opSetPersPresence = null; - - /** - * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt> - * that created us and that we'll use for retrieving the underlying aim - * connection. - */ - OperationSetTypingNotificationsYahooImpl( - ProtocolProviderServiceYahooImpl provider) - { - super(provider); - - provider.addRegistrationStateChangeListener(new ProviderRegListener()); - } - - /** - * Sends a notification to <tt>notifiedContatct</tt> that we have entered - * <tt>typingState</tt>. - * - * @param notifiedContact the <tt>Contact</tt> to notify - * @param typingState the typing state that we have entered. - * - * @throws java.lang.IllegalStateException if the underlying stack is - * not registered and initialized. - * @throws java.lang.IllegalArgumentException if <tt>notifiedContact</tt> is - * not an instance belonging to the underlying implementation. - */ - public void sendTypingNotification(Contact notifiedContact, int typingState) - throws IllegalStateException, IllegalArgumentException - { - assertConnected(); - - if( !(notifiedContact instanceof ContactYahooImpl) ) - throw new IllegalArgumentException( - "The specified contact is not an yahoo contact." - + notifiedContact); - - if(typingState == OperationSetTypingNotifications.STATE_TYPING) - { - - parentProvider.getYahooSession(). - keyTyped(notifiedContact.getAddress(), - parentProvider.getAccountID().getUserID()); - } - else - if(typingState == OperationSetTypingNotifications.STATE_STOPPED || - typingState == OperationSetTypingNotifications.STATE_PAUSED) - { - parentProvider.getYahooSession(). - stopTyping(notifiedContact.getAddress(), - parentProvider.getAccountID().getUserID()); - } - } - - private class TypingListener - extends SessionAdapter - { - @Override - public void notifyReceived(SessionNotifyEvent evt) - { - if(evt.isTyping()) - { - String typingUserID = evt.getFrom(); - - if(typingUserID != null) - { - Contact sourceContact = - opSetPersPresence.findContactByID(typingUserID); - - if(sourceContact == null) - return; - - // typing on - fireTypingNotificationsEvent( - sourceContact, - (evt.getMode() == 1) ? STATE_TYPING : STATE_STOPPED); - } - } - } - } - - /** - * Our listener that will tell us when we're registered and - * ready to accept us as a listener. - */ - private class ProviderRegListener - implements RegistrationStateChangeListener - { - /** - * The method is called by a ProtocolProvider implementation whenever - * a change in the registration state of the corresponding provider had - * occurred. - * @param evt ProviderStatusChangeEvent the event describing the status - * change. - */ - public void registrationStateChanged(RegistrationStateChangeEvent evt) - { - if (logger.isDebugEnabled()) - logger.debug("The provider changed state from: " - + evt.getOldState() - + " to: " + evt.getNewState()); - if (evt.getNewState() == RegistrationState.REGISTERED) - { - opSetPersPresence = - (OperationSetPersistentPresenceYahooImpl) parentProvider - .getOperationSet(OperationSetPersistentPresence.class); - - parentProvider - .getYahooSession().addSessionListener(new TypingListener()); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolIconYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolIconYahooImpl.java deleted file mode 100644 index 34befc9..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolIconYahooImpl.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -import org.jitsi.service.resources.*; -import org.osgi.framework.*; - -/** - * Represents the Yahoo protocol icon. Implements the <tt>ProtocolIcon</tt> - * interface in order to provide an Yahoo icon image in two different sizes. - * - * @author Yana Stamcheva - */ -public class ProtocolIconYahooImpl - implements ProtocolIcon -{ - private static Logger logger = Logger.getLogger(ProtocolIconYahooImpl.class); - - private static ResourceManagementService resourcesService; - - /** - * 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, - getImageInBytes("service.protocol.yahoo.YAHOO_16x16")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_32x32, - getImageInBytes("service.protocol.yahoo.YAHOO_32x32")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_48x48, - getImageInBytes("service.protocol.yahoo.YAHOO_48x48")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_64x64, - getImageInBytes("service.protocol.yahoo.YAHOO_64x64")); - } - - /** - * A hash table containing the protocol icon in different sizes. - */ - private static Hashtable<String, String> iconPathsTable - = new Hashtable<String, String>(); - static - { - iconPathsTable.put(ProtocolIcon.ICON_SIZE_16x16, - getResources().getImagePath("service.protocol.yahoo.YAHOO_16x16")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_32x32, - getResources().getImagePath("service.protocol.yahoo.YAHOO_32x32")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_48x48, - getResources().getImagePath("service.protocol.yahoo.YAHOO_48x48")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_64x64, - getResources().getImagePath("service.protocol.yahoo.YAHOO_64x64")); - } - - /** - * Implements the <tt>ProtocolIcon.getSupportedSizes()</tt> method. Returns - * an iterator to a set containing the supported icon sizes. - * @return an iterator to a set containing the supported icon sizes - */ - public Iterator<String> getSupportedSizes() - { - return iconsTable.keySet().iterator(); - } - - /** - * Returns TRUE if a icon with the given size is supported, FALSE-otherwise. - */ - public boolean isSizeSupported(String iconSize) - { - return iconsTable.containsKey(iconSize); - } - - /** - * Returns the icon image in the given size. - * @param iconSize the icon size; one of ICON_SIZE_XXX constants - */ - public byte[] getIcon(String iconSize) - { - return iconsTable.get(iconSize); - } - - /** - * Returns a path to the icon with the given size. - * @param iconSize the size of the icon we're looking for - * @return the path to the icon with the given size - */ - public String getIconPath(String iconSize) - { - return iconPathsTable.get(iconSize); - } - - /** - * Returns the icon image used to represent the protocol connecting state. - * @return the icon image used to represent the protocol connecting state - */ - public byte[] getConnectingIcon() - { - return getImageInBytes("yahooConnectingIcon"); - } - - /** - * Returns the byte representation of the image corresponding to the given - * identifier. - * - * @param imageID the identifier of the image - * @return the byte representation of the image corresponding to the given - * identifier. - */ - public static byte[] getImageInBytes(String imageID) - { - InputStream in = getResources().getImageInputStream(imageID); - - if (in == null) - return null; - byte[] image = null; - try - { - image = new byte[in.available()]; - - in.read(image); - } - catch (IOException e) - { - logger.error("Failed to load image:" + imageID, e); - } - - return image; - } - - public static ResourceManagementService getResources() - { - if (resourcesService == null) - { - ServiceReference serviceReference = YahooActivator.getBundleContext() - .getServiceReference(ResourceManagementService.class.getName()); - - if(serviceReference == null) - return null; - - resourcesService = (ResourceManagementService) - YahooActivator.getBundleContext().getService(serviceReference); - } - - return resourcesService; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java deleted file mode 100644 index 358286c..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -import org.osgi.framework.*; - -/** - * The Yahoo implementation of the ProtocolProviderFactory. - * @author Damian Minkov - */ -public class ProtocolProviderFactoryYahooImpl - extends ProtocolProviderFactory -{ - - /** - * Creates an instance of the ProtocolProviderFactoryYahooImpl. - */ - protected ProtocolProviderFactoryYahooImpl() - { - super(YahooActivator.getBundleContext(), ProtocolNames.YAHOO); - } - - /** - * Initializes and creates an account corresponding to the specified - * accountProperties and registers the resulting ProtocolProvider in the - * <tt>context</tt> BundleContext parameter. This method has a persistent - * effect. Once created the resulting account will remain installed until - * removed through the uninstall account method. - * - * @param userIDStr the user identifier for the new account - * @param accountProperties a set of protocol (or implementation) - * specific properties defining the new account. - * @return the AccountID of the newly created account - */ - @Override - public AccountID installAccount( String userIDStr, - Map<String, String> accountProperties) - { - BundleContext context - = YahooActivator.getBundleContext(); - if (context == null) - throw new NullPointerException("The specified BundleContext was null"); - - if (userIDStr == null) - throw new NullPointerException("The specified AccountID was null"); - - if (accountProperties == null) - throw new NullPointerException("The specified property map was null"); - - accountProperties.put(USER_ID, userIDStr); - - AccountID accountID = new YahooAccountID(userIDStr, accountProperties); - - //make sure we haven't seen this account id before. - if( registeredAccounts.containsKey(accountID) ) - throw new IllegalStateException( - "An account for id " + userIDStr + " was already installed!"); - - //first store the account and only then load it as the load generates - //an osgi event, the osgi event triggers (through the UI) a call to - //the register() method and it needs to access the configuration service - //and check for a password. - this.storeAccount(accountID, false); - - accountID = loadAccount(accountProperties); - - return accountID; - } - - @Override - protected AccountID createAccountID(String userID, Map<String, String> accountProperties) - { - return new YahooAccountID(userID, accountProperties); - } - - @Override - protected ProtocolProviderService createService(String userID, - AccountID accountID) - { - ProtocolProviderServiceYahooImpl service = - new ProtocolProviderServiceYahooImpl(); - - service.initialize(userID, accountID); - return service; - } - - @Override - public void modifyAccount( ProtocolProviderService protocolProvider, - Map<String, String> accountProperties) - throws NullPointerException - { - BundleContext context - = YahooActivator.getBundleContext(); - - if (context == null) - throw new NullPointerException( - "The specified BundleContext was null"); - - if (protocolProvider == null) - throw new NullPointerException( - "The specified Protocol Provider was null"); - - YahooAccountID accountID - = (YahooAccountID) protocolProvider.getAccountID(); - - // If the given accountID doesn't correspond to an existing account - // we return. - if(!registeredAccounts.containsKey(accountID)) - return; - - ServiceRegistration registration = registeredAccounts.get(accountID); - - // kill the service - if (registration != null) - registration.unregister(); - - if (accountProperties == null) - throw new NullPointerException( - "The specified property map was null"); - - accountProperties.put(USER_ID, accountID.getUserID()); - - if (!accountProperties.containsKey(PROTOCOL)) - accountProperties.put(PROTOCOL, ProtocolNames.YAHOO); - - accountID.setAccountProperties(accountProperties); - - // First store the account and only then load it as the load generates - // an osgi event, the osgi event triggers (trhgough the UI) a call to - // the register() method and it needs to acces the configuration service - // and check for a password. - this.storeAccount(accountID); - - Hashtable<String, String> properties = new Hashtable<String, String>(); - properties.put(PROTOCOL, ProtocolNames.YAHOO); - properties.put(USER_ID, accountID.getUserID()); - - ((ProtocolProviderServiceYahooImpl)protocolProvider) - .initialize(accountID.getUserID(), accountID); - - // We store again the account in order to store all properties added - // during the protocol provider initialization. - this.storeAccount(accountID); - - registration - = context.registerService( - ProtocolProviderService.class.getName(), - protocolProvider, - properties); - - registeredAccounts.put(accountID, registration); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java deleted file mode 100644 index 264aaab..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; - -import net.java.sip.communicator.service.dns.*; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; -import ymsg.network.*; -import ymsg.network.event.*; - -/** - * An implementation of the protocol provider service over the Yahoo protocol - * - * @author Damian Minkov - */ -public class ProtocolProviderServiceYahooImpl - extends AbstractProtocolProviderService -{ - /** - * This class logger. - */ - private static final Logger logger = - Logger.getLogger(ProtocolProviderServiceYahooImpl.class); - - /** - * The current yahoo session. - */ - private YahooSession yahooSession = null; - - /** - * indicates whether or not the provider is initialized and ready for use. - */ - private boolean isInitialized = false; - - /** - * We use this to lock access to initialization. - */ - private final Object initializationLock = new Object(); - - /** - * The identifier of the account that this provider represents. - */ - private AccountID accountID = null; - - /** - * Used when we need to re-register - */ - private SecurityAuthority authority = null; - - /** - * The persistent presence operations set. - */ - private OperationSetPersistentPresenceYahooImpl persistentPresence = null; - - /** - * Typing notifications operations set. - */ - private OperationSetTypingNotificationsYahooImpl typingNotifications = null; - - /** - * The logo corresponding to the msn protocol. - */ - private ProtocolIconYahooImpl yahooIcon - = new ProtocolIconYahooImpl(); - - /** - * The connection listener. - */ - private YahooConnectionListener connectionListener = null; - - /** - * Returns the state of the registration of this protocol provider - * @return the <tt>RegistrationState</tt> that this provider is - * currently in or null in case it is in a unknown state. - */ - public RegistrationState getRegistrationState() - { - if(yahooSession != null && - yahooSession.getSessionStatus() == StatusConstants.MESSAGING) - return RegistrationState.REGISTERED; - else - return RegistrationState.UNREGISTERED; - } - - /** - * Starts the registration process. Connection details such as - * registration server, user name/number are provided through the - * configuration service through implementation specific properties. - * - * @param authority the security authority that will be used for resolving - * any security challenges that may be returned during the - * registration or at any moment while wer're registered. - * @throws OperationFailedException with the corresponding code it the - * registration fails for some reason (e.g. a networking error or an - * implementation problem). - */ - public void register(final SecurityAuthority authority) - throws OperationFailedException - { - if(authority == null) - throw new IllegalArgumentException( - "The register method needs a valid non-null authority impl " - + " in order to be able and retrieve passwords."); - - this.authority = authority; - - connectAndLogin(authority, SecurityAuthority.AUTHENTICATION_REQUIRED); - } - - /** - * Connects and logins to the server - * @param authority SecurityAuthority - * @param authReasonCode the authentication reason code, which should - * indicate why are making an authentication request - * @throws OperationFailedException if login parameters - * as server port are not correct - */ - private void connectAndLogin( SecurityAuthority authority, - int authReasonCode) - throws OperationFailedException - { - synchronized(initializationLock) - { - //verify whether a password has already been stored for this account - String password = YahooActivator. - getProtocolProviderFactory().loadPassword(getAccountID()); - - // If the password hasn't been saved or the reason is one of those - // listed below we need to ask the user for credentials again. - if (password == null - || authReasonCode == SecurityAuthority.WRONG_PASSWORD - || authReasonCode == SecurityAuthority.WRONG_USERNAME) - { - //create a default credentials object - UserCredentials credentials = new UserCredentials(); - credentials.setUserName(getAccountID().getUserID()); - - //request a password from the user - credentials = authority.obtainCredentials( - getAccountID().getDisplayName(), - credentials, - authReasonCode); - - // in case user has canceled the login window - if(credentials == null) - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.UNREGISTERED, - RegistrationStateChangeEvent.REASON_USER_REQUEST, ""); - return; - } - - //extract the password the user passed us. - char[] pass = credentials.getPassword(); - - // the user didn't provide us a password (canceled the operation) - if(pass == null) - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.UNREGISTERED, - RegistrationStateChangeEvent.REASON_USER_REQUEST, ""); - return; - } - password = new String(pass); - - if (credentials.isPasswordPersistent()) - { - YahooActivator.getProtocolProviderFactory() - .storePassword(getAccountID(), password); - } - } - - yahooSession = new YahooSession(); - connectionListener = new YahooConnectionListener(); - yahooSession.addSessionListener(connectionListener); - - try - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.REGISTERING, - RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); - - yahooSession.login(getAccountID().getUserID(), password); - - if(yahooSession.getSessionStatus()==StatusConstants.MESSAGING) - { - persistentPresence.fireProviderStatusChangeEvent( - persistentPresence.getPresenceStatus(), - persistentPresence.yahooStatusToPresenceStatus( - yahooSession.getStatus())); - - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.REGISTERED, - RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); - } - else - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.UNREGISTERED, - RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); - } - } - catch (LoginRefusedException ex) - { - if(ex.getStatus() == StatusConstants.STATUS_BADUSERNAME) - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.AUTHENTICATION_FAILED, - RegistrationStateChangeEvent.REASON_NON_EXISTING_USER_ID, - null); - - reregister(SecurityAuthority.WRONG_USERNAME); - } - else if(ex.getStatus() == StatusConstants.STATUS_BAD) - { - YahooActivator.getProtocolProviderFactory() - .storePassword(getAccountID(), null); - - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.AUTHENTICATION_FAILED, - RegistrationStateChangeEvent.REASON_AUTHENTICATION_FAILED, - null); - - // Try to re-register and ask the user to retype the password. - reregister(SecurityAuthority.WRONG_PASSWORD); - } - else if(ex.getStatus() == StatusConstants.STATUS_LOCKED) - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.AUTHENTICATION_FAILED, - RegistrationStateChangeEvent.REASON_RECONNECTION_RATE_LIMIT_EXCEEDED, - null); - } - } - catch (IOException ex) - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.CONNECTION_FAILED, - RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); - } - catch (DnssecRuntimeException ex) - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.UNREGISTERED, - RegistrationStateChangeEvent.REASON_USER_REQUEST, null); - } - } - } - - /** - * Reconnects if fails fire connection failed. - * @param reasonCode the appropriate <tt>SecurityAuthority</tt> reasonCode, - * which would specify the reason for which we're re-calling the login. - */ - void reregister(int reasonCode) - { - try - { - connectAndLogin(authority, reasonCode); - } - catch (OperationFailedException ex) - { - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.CONNECTION_FAILED, - RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); - } - } - - /** - * Ends the registration of this protocol provider with the service. - */ - public void unregister() - { - unregisterInternal(true); - } - - /** - * Unregister and fire the event if requested - * @param fireEvent boolean - */ - void unregisterInternal(boolean fireEvent) - { - RegistrationState currRegState = getRegistrationState(); - - if(fireEvent) - fireRegistrationStateChanged( - currRegState, - RegistrationState.UNREGISTERING, - RegistrationStateChangeEvent.REASON_USER_REQUEST, - null); - - try - { - if(connectionListener != null && yahooSession != null) - { - yahooSession.removeSessionListener(connectionListener); - connectionListener = null; - } - - if((yahooSession != null) - && (yahooSession.getSessionStatus() == StatusConstants.MESSAGING)) - yahooSession.logout(); - } - catch(Exception ex) - { - logger.error("Cannot logout! ", ex); - } - - yahooSession = null; - - if(fireEvent) - fireRegistrationStateChanged( - currRegState, - RegistrationState.UNREGISTERED, - RegistrationStateChangeEvent.REASON_USER_REQUEST, - null); - } - - /* - * (non-Javadoc) - * - * @see net.java.sip.communicator.service.protocol.ProtocolProviderService# - * isSignallingTransportSecure() - */ - public boolean isSignalingTransportSecure() - { - return false; - } - - /** - * Returns the "transport" protocol of this instance used to carry the - * control channel for the current protocol service. - * - * @return The "transport" protocol of this instance: TCP. - */ - public TransportProtocol getTransportProtocol() - { - return TransportProtocol.TCP; - } - - /** - * Returns the short name of the protocol that the implementation of this - * provider is based upon (like SIP, Msn, ICQ/AIM, or others for - * example). - * - * @return a String containing the short name of the protocol this - * service is taking care of. - */ - public String getProtocolName() - { - return ProtocolNames.YAHOO; - } - - /** - * Initialized the service implementation, and puts it in a sate where it - * could interoperate with other services. It is strongly recomended that - * properties in this Map be mapped to property names as specified by - * <tt>AccountProperties</tt>. - * - * @param screenname the account id/uin/screenname of the account that - * we're about to create - * @param accountID the identifier of the account that this protocol - * provider represents. - * - * @see net.java.sip.communicator.service.protocol.AccountID - */ - protected void initialize(String screenname, - AccountID accountID) - { - synchronized(initializationLock) - { - this.accountID = accountID; - - addSupportedOperationSet( - OperationSetInstantMessageTransform.class, - new OperationSetInstantMessageTransformImpl()); - - //initialize the presence operationset - persistentPresence - = new OperationSetPersistentPresenceYahooImpl(this); - addSupportedOperationSet( - OperationSetPersistentPresence.class, - persistentPresence); - //register it once again for those that simply need presence - addSupportedOperationSet( - OperationSetPresence.class, - persistentPresence); - - //initialize the IM operation set - addSupportedOperationSet( - OperationSetBasicInstantMessaging.class, - new OperationSetBasicInstantMessagingYahooImpl(this)); - - //initialize the multi user chat operation set - addSupportedOperationSet( - OperationSetAdHocMultiUserChat.class, - new OperationSetAdHocMultiUserChatYahooImpl(this)); - - //initialize the typing notifications operation set - typingNotifications - = new OperationSetTypingNotificationsYahooImpl(this); - addSupportedOperationSet( - OperationSetTypingNotifications.class, - typingNotifications); - - addSupportedOperationSet( - OperationSetFileTransfer.class, - new OperationSetFileTransferYahooImpl(this)); - - isInitialized = true; - } - } - - /** - * Makes the service implementation close all open sockets and release - * any resources that it might have taken and prepare for - * shutdown/garbage collection. - */ - public void shutdown() - { - synchronized(initializationLock){ - unregisterInternal(false); - yahooSession = null; - isInitialized = false; - } - } - - /** - * Returns true if the provider service implementation is initialized and - * ready for use by other services, and false otherwise. - * - * @return true if the provider is initialized and ready for use and false - * otherwise - */ - public boolean isInitialized() - { - return isInitialized; - } - - /** - * Returns the AccountID that uniquely identifies the account represented - * by this instance of the ProtocolProviderService. - * @return the id of the account represented by this provider. - */ - public AccountID getAccountID() - { - return accountID; - } - - /** - * Returns the Yahoo<tt>Session</tt>opened by this provider - * @return a reference to the <tt>Session</tt> last opened by this - * provider. - */ - YahooSession getYahooSession() - { - return yahooSession; - } - - /** - * Creates a RegistrationStateChange event corresponding to the specified - * old and new states and notifies all currently registered listeners. - * - * @param oldState the state that the provider had before the change - * occurred - * @param newState the state that the provider is currently in. - * @param reasonCode a value corresponding to one of the REASON_XXX fields - * of the RegistrationStateChangeEvent class, indicating the reason for - * this state transition. - * @param reason a String further explaining the reason code or null if - * no such explanation is necessary. - */ - @Override - public void fireRegistrationStateChanged( RegistrationState oldState, - RegistrationState newState, - int reasonCode, - String reason) - { - if(newState.equals(RegistrationState.UNREGISTERED)) - { - unregisterInternal(false); - yahooSession = null; - } - - super.fireRegistrationStateChanged(oldState, newState, reasonCode, reason); - } - - /** - * Listens when we are logged in the server - * or incoming exception in the lib impl. - */ - private class YahooConnectionListener - extends SessionAdapter - { - /** - * Yahoo has logged us off the system, or the connection was lost - * - * @param ev the event - */ - @Override - public void connectionClosed(SessionEvent ev) - { - if(isRegistered()) - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.CONNECTION_FAILED, - RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null); - } - - /** - * Some exception has occurred in stack. - * @param ev - */ - @Override - public void inputExceptionThrown(SessionExceptionEvent ev) - { - if(ev.getException() instanceof YMSG9BadFormatException) - { - logger.error("Yahoo protocol exception occured exception", - ev.getException()); - logger.error("Yahoo protocol exception occured exception cause", - ev.getException().getCause()); - } - else - logger.error( - "Yahoo protocol exception occured", ev.getException()); - - unregisterInternal(false); - if(isRegistered()) - fireRegistrationStateChanged( - getRegistrationState(), - RegistrationState.UNREGISTERED, - RegistrationStateChangeEvent.REASON_INTERNAL_ERROR, null); - } - } - - /** - * Returns the yahoo protocol icon. - * @return the yahoo protocol icon - */ - public ProtocolIcon getProtocolIcon() - { - return yahooIcon; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java deleted file mode 100644 index 856bb30..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * A dummy ContactGroup implementation representing the ContactList root for - * Yahoo contact lists. - * @author Damian Minkov - */ -public class RootContactGroupYahooImpl - extends AbstractContactGroupYahooImpl -{ - private String ROOT_CONTACT_GROUP_NAME = "ContactListRoot"; - private List<ContactGroup> subGroups = new LinkedList<ContactGroup>(); - private boolean isResolved = false; - - /** - * An empty list that we use when returning an iterator. - */ - private List<Contact> dummyContacts = new LinkedList<Contact>(); - - private final ProtocolProviderServiceYahooImpl protocolProvider; - - /** - * Creates a ContactGroup instance. - */ - RootContactGroupYahooImpl(ProtocolProviderServiceYahooImpl protocolProvider) - { - this.protocolProvider = protocolProvider; - } - - /** - * The ContactListRoot is the only group that can contain subgroups. - * - * @return true (always) - */ - public boolean canContainSubgroups() - { - return true; - } - - /** - * Returns the name of this group which is always - * <tt>ROOT_CONTACT_GROUP_NAME</tt>. - * - * @return a String containing the name of this group. - */ - public String getGroupName() - { - return ROOT_CONTACT_GROUP_NAME; - } - - /** - * Adds the specified group to the end of the list of sub groups. - * @param group the group to add. - */ - void addSubGroup(ContactGroupYahooImpl group) - { - subGroups.add(group); - } - - /** - * Removes the specified from the list of sub groups - * @param group the group to remove. - */ - void removeSubGroup(ContactGroupYahooImpl group) - { - removeSubGroup(subGroups.indexOf(group)); - } - - /** - * Removes the sub group with the specified index. - * @param index the index of the group to remove - */ - void removeSubGroup(int index) - { - subGroups.remove(index); - } - - /** - * Returns the number of subgroups contained by this - * <tt>RootContactGroupImpl</tt>. - * - * @return an int indicating the number of subgroups that this - * ContactGroup contains. - */ - public int countSubgroups() - { - return subGroups.size(); - } - - /** - * Returns null as this is the root contact group. - * @return null as this is the root contact group. - */ - public ContactGroup getParentContactGroup() - { - return null; - } - - /** - * Returns the subgroup with the specified index. - * - * @param index the index of the <tt>ContactGroup</tt> to retrieve. - * @return the <tt>ContactGroup</tt> with the specified index. - */ - public ContactGroup getGroup(int index) - { - return 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<ContactGroup> subgroups = subgroups(); - while (subgroups.hasNext()) - { - ContactGroup grp = subgroups.next(); - - if (grp.getGroupName().equals(groupName)) - return grp; - } - - return null; - } - - /** - * Returns an iterator over the sub groups that this - * <tt>ContactGroup</tt> contains. - * - * @return a java.util.Iterator over the <tt>ContactGroup</tt> - * children of this group (i.e. subgroups). - */ - public Iterator<ContactGroup> subgroups() - { - return subGroups.iterator(); - } - - /** - * Returns the number, which is always 0, of <tt>Contact</tt> members - * of this <tt>ContactGroup</tt> - * @return an int indicating the number of <tt>Contact</tt>s, members - * of this <tt>ContactGroup</tt>. - */ - public int countContacts() - { - return dummyContacts.size(); - } - - /** - * Returns an Iterator over all contacts, member of this - * <tt>ContactGroup</tt>. - * @return a java.util.Iterator over all contacts inside this - * <tt>ContactGroup</tt> - */ - public Iterator<Contact> contacts() - { - return dummyContacts.iterator(); - } - - /** - * Returns the <tt>Contact</tt> with the specified address or - * identifier. - * @param id the addres or identifier of the <tt>Contact</tt> we are - * looking for. - * @return the <tt>Contact</tt> with the specified id or address. - */ - public Contact getContact(String id) - { - //no contacts in the root group for this yahoo impl. - return null; - } - - - /** - * Returns a string representation of the root contact group that contains - * all subgroups and subcontacts of this group. - * - * @return a string representation of this root contact group. - */ - @Override - public String toString() - { - StringBuffer buff = new StringBuffer(getGroupName()); - buff.append(".subGroups=" + countSubgroups() + ":\n"); - - Iterator<ContactGroup> subGroups = subgroups(); - while (subGroups.hasNext()) - { - ContactGroup group = subGroups.next(); - buff.append(group.toString()); - if (subGroups.hasNext()) - buff.append("\n"); - } - return buff.toString(); - } - - /** - * Returns the protocol provider that this group belongs to. - * @return a regerence to the ProtocolProviderService instance that this - * ContactGroup belongs to. - */ - public ProtocolProviderService getProtocolProvider() - { - return protocolProvider; - } - - /** - * Determines whether or not this contact group is being stored by the - * server. Non persistent contact groups exist for the sole purpose of - * containing non persistent contacts. - * @return true if the contact group is persistent and false otherwise. - */ - public boolean isPersistent() - { - return true; - } - - /** - * Returns null as no persistent data is required and the group name is - * sufficient for restoring the contact. - * <p> - * @return null as no such data is needed. - */ - public String getPersistentData() - { - return null; - } - - /** - * Determines whether or not this group has been resolved against the - * server. Unresolved groups are used when initially loading a contact - * list that has been stored in a local file until the presence operation - * set has managed to retrieve all the contact list from the server and has - * properly mapped groups to their on-line buddies. - * @return true if the group has been resolved (mapped against a buddy) - * and false otherwise. - */ - public boolean isResolved() - { - return isResolved; - } - - /** - * Returns a <tt>String</tt> that uniquely represnets the group. In this we - * use the name of the group as an identifier. This may cause problems - * though, in clase the name is changed by some other application between - * consecutive runs of the sip-communicator. - * - * @return a String representing this group in a unique and persistent - * way. - */ - public String getUID() - { - return getGroupName(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java deleted file mode 100644 index e732b84..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java +++ /dev/null @@ -1,1274 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; -import ymsg.network.*; -import ymsg.network.event.*; - -/** - * This class encapsulates the Roster class. Once created, it will - * register itself as a listener to the encapsulated Roster and modify it's - * local copy of Contacts and ContactGroups every time an event is generated - * by the underlying framework. The class would also generate - * corresponding sip-communicator events to all events coming from smack. - * - * @author Damian Minkov - * @author Emil Ivov - */ -public class ServerStoredContactListYahooImpl -{ - private static final Logger logger = - Logger.getLogger(ServerStoredContactListYahooImpl.class); - - /** - * If there is no group and we add contact with no parent - * a default group is created with name : DEFAULT_GROUP_NAME - */ - private static final String DEFAULT_GROUP_NAME = "General"; - - /** - * The root contagroup. The container for all yahoo buddies and groups. - */ - private final RootContactGroupYahooImpl rootGroup; - - /** - * The operation set that created us and that we could use when dispatching - * subscription events. - */ - private final OperationSetPersistentPresenceYahooImpl parentOperationSet; - - /** - * The provider that is on top of us. - */ - private final ProtocolProviderServiceYahooImpl yahooProvider; - - private YahooSession yahooSession = null; - - /** - * Listeners that would receive event notifications for changes in group - * names or other properties, removal or creation of groups. - */ - private Vector<ServerStoredGroupListener> serverStoredGroupListeners - = new Vector<ServerStoredGroupListener>(); - - private ContactListModListenerImpl contactListModListenerImpl - = new ContactListModListenerImpl(); - - /** - * Handler for incoming authorization requests. - */ - private AuthorizationHandler handler = null; - - private Hashtable<String, String> addedCustomYahooIds - = new Hashtable<String, String>(); - - /** - * Creates a ServerStoredContactList wrapper for the specified BuddyList. - * - * @param parentOperationSet the operation set that created us and that - * we could use for dispatching subscription events - * @param provider the provider that has instantiated us. - */ - ServerStoredContactListYahooImpl( - OperationSetPersistentPresenceYahooImpl parentOperationSet, - ProtocolProviderServiceYahooImpl provider) - { - //We need to init these as early as possible to ensure that the provider - //and the operationsset would not be null in the incoming events. - this.parentOperationSet = parentOperationSet; - - this.yahooProvider = provider; - this.rootGroup = new RootContactGroupYahooImpl(this.yahooProvider); - } - - /** - * Handler for incoming authorization requests. - * - * @param handler an instance of an AuthorizationHandler for - * authorization requests coming from other users requesting - * permission add us to their contact list. - */ - public void setAuthorizationHandler(AuthorizationHandler handler) - { - this.handler = handler; - } - - /** - * Returns the root group of the contact list. - * - * @return the root ContactGroup for the ContactList - */ - public ContactGroup getRootGroup() - { - return rootGroup; - } - - /** - * Registers the specified group listener so that it would receive events - * on group modification/creation/destruction. - * @param listener the ServerStoredGroupListener to register for group - * events - */ - void addGroupListener(ServerStoredGroupListener listener) - { - synchronized(serverStoredGroupListeners) - { - if(!serverStoredGroupListeners.contains(listener)) - serverStoredGroupListeners.add(listener); - } - } - - /** - * Removes the specified group listener so that it won't receive further - * events on group modification/creation/destruction. - * @param listener the ServerStoredGroupListener to unregister - */ - void removeGroupListener(ServerStoredGroupListener listener) - { - synchronized(serverStoredGroupListeners) - { - this.serverStoredGroupListeners.remove(listener); - } - } - - /** - * Creates the corresponding event and notifies all - * <tt>ServerStoredGroupListener</tt>s that the source group has been - * removed, changed, renamed or whatever happened to it. - * @param group the ContactGroup that has been created/modified/removed - * @param eventID the id of the event to generate. - */ - private void fireGroupEvent(ContactGroupYahooImpl group, int eventID) - { - //bail out if no one's listening - if(parentOperationSet == null){ - if (logger.isDebugEnabled()) - logger.debug("No presence op. set available. Bailing out."); - return; - } - - ServerStoredGroupEvent evt = new ServerStoredGroupEvent( - group - , eventID - , parentOperationSet.getServerStoredContactListRoot() - , yahooProvider - , parentOperationSet); - - if (logger.isTraceEnabled()) - logger.trace("Will dispatch the following grp event: " + evt); - - Iterable<ServerStoredGroupListener> listeners; - synchronized (serverStoredGroupListeners) - { - listeners - = new ArrayList<ServerStoredGroupListener>( - serverStoredGroupListeners); - } - - for (ServerStoredGroupListener listener : listeners) - { - try{ - if (eventID == ServerStoredGroupEvent.GROUP_REMOVED_EVENT) - listener.groupRemoved(evt); - else if (eventID == ServerStoredGroupEvent.GROUP_RENAMED_EVENT) - listener.groupNameChanged(evt); - else if (eventID == ServerStoredGroupEvent.GROUP_CREATED_EVENT) - listener.groupCreated(evt); - else if (eventID == ServerStoredGroupEvent.GROUP_RESOLVED_EVENT) - listener.groupResolved(evt); - }catch(Exception ex){ - logger.warn("Unhandled Exception! ", ex); - } - } - } - - /** - * Make the parent persistent presence operation set dispatch a contact - * removed event. - * @param parentGroup the group where that the removed contact belonged to. - * @param contact the contact that was removed. - */ - private void fireContactRemoved( ContactGroup parentGroup, - ContactYahooImpl contact) - { - //bail out if no one's listening - if(parentOperationSet == null){ - if (logger.isDebugEnabled()) - logger.debug("No presence op. set available. Bailing out."); - return; - } - - //dispatch - parentOperationSet.fireSubscriptionEvent( - contact, parentGroup, SubscriptionEvent.SUBSCRIPTION_REMOVED); - } - - /** - * Make the parent persistent presence operation set dispatch a subscription - * moved event. - * @param oldParentGroup the group where the source contact was located - * before being moved - * @param newParentGroup the group that the source contact is currently in. - * @param contact the contact that was added - */ - private void fireContactMoved( ContactGroup oldParentGroup, - ContactGroupYahooImpl newParentGroup, - ContactYahooImpl contact) - { - //bail out if no one's listening - if(parentOperationSet == null){ - if (logger.isDebugEnabled()) - logger.debug("No presence op. set available. Bailing out."); - return; - } - - //dispatch - parentOperationSet.fireSubscriptionMovedEvent( - contact, oldParentGroup, newParentGroup); - } - - /** - * Returns a reference to the provider that created us. - * @return a reference to a ProtocolProviderServiceImpl instance. - */ - ProtocolProviderServiceYahooImpl getParentProvider() - { - return yahooProvider; - } - - /** - * Returns the ConntactGroup with the specified name or null if no such - * group was found. - * <p> - * @param name the name of the group we're looking for. - * @return a reference to the ContactGroupYahooImpl instance we're looking - * for or null if no such group was found. - */ - public ContactGroupYahooImpl findContactGroup(String name) - { - String nameToLookFor = replaceIllegalChars(name); - Iterator<ContactGroup> contactGroups = rootGroup.subgroups(); - - while(contactGroups.hasNext()) - { - ContactGroupYahooImpl contactGroup - = (ContactGroupYahooImpl) contactGroups.next(); - - if (contactGroup.getGroupName().equals(nameToLookFor)) - return contactGroup; - } - - return null; - } - - /** - * Returns the Contact with the specified id or null if - * no such id was found. - * - * @param id the id of the contact to find. - * @return the <tt>Contact</tt> carrying the specified - * <tt>screenName</tt> or <tt>null</tt> if no such contact exits. - */ - public ContactYahooImpl findContactById(String id) - { - Iterator<ContactGroup> contactGroups = rootGroup.subgroups(); - ContactYahooImpl result = null; - - while(contactGroups.hasNext()) - { - ContactGroupYahooImpl contactGroup - = (ContactGroupYahooImpl) contactGroups.next(); - - result = contactGroup.findContact(id); - - if (result != null) - return result; - } - - return null; - } - - /** - * Returns the Contact corresponding to the specified <tt>YahooUser</tt> - * or null if no such id was found. - * - * @param yahooUser the YahooUser of the contact to find. - * @return the <tt>Contact</tt> carrying the specified - * <tt>screenName</tt> or <tt>null</tt> if no such contact exits. - */ - public ContactYahooImpl findContactByYahooUser(YahooUser yahooUser) - { - return findContactById(yahooUser.getId().toLowerCase()); - } - - /** - * Returns the ContactGroup containing the specified contact or null - * if no such group or contact exist. - * - * @param child the contact whose parent group we're looking for. - * @return the <tt>ContactGroup</tt> containing the specified - * <tt>contact</tt> or <tt>null</tt> if no such groupo or contact - * exist. - */ - public ContactGroup findContactGroup(ContactYahooImpl child) - { - Iterator<ContactGroup> contactGroups = rootGroup.subgroups(); - String contactAddress = child.getAddress(); - - while(contactGroups.hasNext()) - { - ContactGroupYahooImpl contactGroup - = (ContactGroupYahooImpl) contactGroups.next(); - - if( contactGroup.findContact(contactAddress)!= null) - return contactGroup; - } - - return null; - } - - /** - * Adds a new contact with the specified screenname to the list under a - * default location. - * @param id the id of the contact to add. - * @throws OperationFailedException - */ - public void addContact(String id) - throws OperationFailedException - { - ContactGroupYahooImpl parent = getFirstPersistentGroup(); - - if(parent == null) - { - // if there is no group create it - parent = createUnresolvedContactGroup(DEFAULT_GROUP_NAME); - } - - addContact(parent, id); - } - - /** - * Adds a new contact with the specified screenname to the list under the - * specified group. - * @param id the id of the contact to add. - * @param parent the group under which we want the new contact placed. - * @throws OperationFailedException if the contact already exist - */ - public void addContact(final ContactGroupYahooImpl parent, String id) - throws OperationFailedException - { - if (logger.isTraceEnabled()) - logger.trace("Adding contact " + id + " to parent=" + parent); - - //if the contact is already in the contact list and is not volatile, - //then only broadcast an event - ContactYahooImpl existingContact = findContactById(id); - - if( existingContact != null - && existingContact.isPersistent() ) - { - if (logger.isDebugEnabled()) - logger.debug("Contact " + id + " already exists."); - throw new OperationFailedException( - "Contact " + id + " already exists.", - OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS); - } - - if(id.indexOf("@") > -1 ) - addedCustomYahooIds.put(YahooSession.getYahooUserID(id), id); - - try - { - yahooSession.addFriend(YahooSession.getYahooUserID(id), - parent.getGroupName()); - } - catch(IOException ex) - { - throw new OperationFailedException( - "Contact cannot be added " + id, - OperationFailedException.NETWORK_FAILURE); - } - } - - /** - * Creates a non persistent contact for the specified address. This would - * also create (if necessary) a group for volatile contacts that would not - * be added to the server stored contact list. This method would have no - * effect on the server stored contact list. - * @param id the address of the contact to create. - * @return the newly created volatile <tt>ContactImpl</tt> - */ - ContactYahooImpl createVolatileContact(String id) - { - if (logger.isTraceEnabled()) - logger.trace("Creating volatile contact " + id); - ContactYahooImpl newVolatileContact = - new ContactYahooImpl(id, this, false, false, true); - - //Check whether a volatile group already exists and if not create one - ContactGroupYahooImpl theVolatileGroup = getNonPersistentGroup(); - - //if the parent group is null then create it - if (theVolatileGroup == null) - { - theVolatileGroup = new VolatileContactGroupYahooImpl( - YahooActivator.getResources().getI18NString( - "service.gui.NOT_IN_CONTACT_LIST_GROUP_NAME"), - this); - - theVolatileGroup.addContact(newVolatileContact); - - this.rootGroup.addSubGroup(theVolatileGroup); - - fireGroupEvent(theVolatileGroup - , ServerStoredGroupEvent.GROUP_CREATED_EVENT); - } - else - { - theVolatileGroup.addContact(newVolatileContact); - - fireContactAdded(theVolatileGroup, newVolatileContact); - } - - return newVolatileContact; - } - - - /** - * Creates a non resolved contact for the specified address and inside the - * specified group. The newly created contact would be added to the local - * contact list as a standard contact but when an event is received from the - * server concerning this contact, then it will be reused and only its - * isResolved field would be updated instead of creating the whole contact - * again. - * - * @param parentGroup the group where the unersolved contact is to be - * created - * @param id the Address of the contact to create. - * @return the newly created unresolved <tt>ContactImpl</tt> - */ - ContactYahooImpl createUnresolvedContact(ContactGroup parentGroup, - String id) - { - if (logger.isTraceEnabled()) - logger.trace("Creating unresolved contact " + id - + " to parent=" + parentGroup); - - ContactYahooImpl existingContact = findContactById(id); - - if( existingContact != null) - { - return existingContact; - } - - ContactYahooImpl newUnresolvedContact - = new ContactYahooImpl(id, this, false, true, false); - - if(parentGroup instanceof ContactGroupYahooImpl) - ((ContactGroupYahooImpl)parentGroup). - addContact(newUnresolvedContact); - - fireContactAdded(parentGroup, newUnresolvedContact); - - return newUnresolvedContact; - } - - /** - * Creates a non resolved contact group for the specified name. The newly - * created group would be added to the local contact list as any other group - * but when an event is received from the server concerning this group, then - * it will be reused and only its isResolved field would be updated instead - * of creating the whole group again. - * <p> - * @param groupName the name of the group to create. - * @return the newly created unresolved <tt>ContactGroupImpl</tt> - */ - ContactGroupYahooImpl createUnresolvedContactGroup(String groupName) - { - ContactGroupYahooImpl existingGroup = findContactGroup(groupName); - - if( existingGroup != null ) - { - if (logger.isDebugEnabled()) - logger.debug("ContactGroup " + groupName + " already exists."); - return existingGroup; - } - - ContactGroupYahooImpl newUnresolvedGroup = - new ContactGroupYahooImpl(groupName, this); - - this.rootGroup.addSubGroup(newUnresolvedGroup); - - fireGroupEvent(newUnresolvedGroup - , ServerStoredGroupEvent.GROUP_CREATED_EVENT); - - return newUnresolvedGroup; - } - - /** - * Creates the specified group on the server stored contact list. - * @param groupName a String containing the name of the new group. - * @throws OperationFailedException with code CONTACT_GROUP_ALREADY_EXISTS - * if the group we're trying to create is already in our contact list. - */ - public void createGroup(String groupName) - throws OperationFailedException - { - if (logger.isTraceEnabled()) - logger.trace("Creating group: " + groupName); - - ContactGroupYahooImpl existingGroup = findContactGroup(groupName); - - if( existingGroup != null && existingGroup.isPersistent() ) - { - if (logger.isDebugEnabled()) - logger.debug("ContactGroup " + groupName + " already exists."); - throw new OperationFailedException( - "ContactGroup " + groupName + " already exists.", - OperationFailedException.CONTACT_GROUP_ALREADY_EXISTS); - } - - // create unresolved group if friend is added - group will be resolved - createUnresolvedContactGroup(groupName); - } - - /** - * Removes the specified group from the buddy list. - * @param groupToRemove the group that we'd like removed. - */ - @SuppressWarnings("unchecked") //jymsg legacy code - public void removeGroup(ContactGroupYahooImpl groupToRemove) - { - // to remove group just remove all the contacts in it - - if (logger.isTraceEnabled()) - logger.trace("removing group " + groupToRemove); - - // if its not persistent group just remove it - if(!groupToRemove.isPersistent() || !groupToRemove.isResolved()) - { - rootGroup.removeSubGroup(groupToRemove); - fireGroupEvent(groupToRemove, - ServerStoredGroupEvent.GROUP_REMOVED_EVENT); - return; - } - - Vector<YahooUser> contacts - = groupToRemove.getSourceGroup().getMembers(); - - if(contacts.size() == 0) - { - // the group is empty just remove it - rootGroup.removeSubGroup(groupToRemove); - fireGroupEvent(groupToRemove, - ServerStoredGroupEvent.GROUP_REMOVED_EVENT); - return; - } - - /* - * ContactGroupYahooImpl#getGroupName() isn't a plain getter so - * performance-wise we're better off not calling it multiple times in - * the following loop. - */ - String groupToRemoveName = groupToRemove.getGroupName(); - - for (YahooUser item : contacts) - { - try - { - yahooSession.removeFriend(item.getId(), groupToRemoveName); - } - catch(IOException ex) - { - if (logger.isInfoEnabled()) - logger.info("Cannot Remove contact " + item.getId()); - } - } - } - - /** - * Removes a contact from the serverside list - * Event will come for successful operation - * @param contactToRemove ContactYahooImpl - */ - void removeContact(ContactYahooImpl contactToRemove) - { - if (logger.isTraceEnabled()) - logger.trace("Removing yahoo contact " - + contactToRemove.getSourceContact()); - - if(contactToRemove.isVolatile()) - { - ContactGroupYahooImpl parent = - (ContactGroupYahooImpl)contactToRemove.getParentContactGroup(); - - parent.removeContact(contactToRemove); - fireContactRemoved(parent, contactToRemove); - return; - } - - try - { - yahooSession.removeFriend( - contactToRemove.getSourceContact().getId(), - contactToRemove.getParentContactGroup().getGroupName()); - } - catch(IOException ex) - { - if (logger.isInfoEnabled()) - logger.info("Cannot Remove contact " + contactToRemove); - } - } - - /** - * Renames the specified group according to the specified new name.. - * @param groupToRename the group that we'd like removed. - * @param newName the new name of the group - */ - public void renameGroup(ContactGroupYahooImpl groupToRename, String newName) - { - // not working - /* - try - { - yahooSession.renameGroup(groupToRename.getGroupName(), newName); - } - catch(IOException ex) - { - if (logger.isInfoEnabled()) - logger.info("Cannot rename group " + groupToRename); - } - - fireGroupEvent(groupToRename, - ServerStoredGroupEvent.GROUP_RENAMED_EVENT); - */ - } - - /** - * Moves the specified <tt>contact</tt> to the group indicated by - * <tt>newParent</tt>. - * @param contact the contact that we'd like moved under the new group. - * @param newParent the group where we'd like the parent placed. - */ - public void moveContact(ContactYahooImpl contact, - ContactGroupYahooImpl newParent) - { - String userID = contact.getID(); - try - { - contactListModListenerImpl. - waitForMove(userID, - contact.getParentContactGroup().getGroupName()); - - yahooSession.addFriend( - userID, - newParent.getGroupName()); - } - catch(IOException ex) - { - contactListModListenerImpl.removeWaitForMove(userID); - logger.error("Contact cannot be added " + ex.getMessage()); - } - } - - /** - * Returns the volatile group - * - * @return ContactGroupYahooImpl - */ - private ContactGroupYahooImpl getNonPersistentGroup() - { - for (int i = 0; i < getRootGroup().countSubgroups(); i++) - { - ContactGroupYahooImpl gr = - (ContactGroupYahooImpl)getRootGroup().getGroup(i); - - if(!gr.isPersistent()) - return gr; - } - - return null; - } - - /** - * Returns the first persistent group - * - * @return ContactGroupIcqImpl - */ - private ContactGroupYahooImpl getFirstPersistentGroup() - { - for (int i = 0; i < getRootGroup().countSubgroups(); i++) - { - ContactGroupYahooImpl gr = - (ContactGroupYahooImpl)getRootGroup().getGroup(i); - - if(gr.isPersistent()) - return gr; - } - - return null; - } - - /** - * Make the parent persistent presence operation set dispatch a contact - * added event. - * @param parentGroup the group where the new contact was added - * @param contact the contact that was added - */ - void fireContactAdded( ContactGroup parentGroup, - ContactYahooImpl contact) - { - //bail out if no one's listening - if(parentOperationSet == null){ - if (logger.isDebugEnabled()) - logger.debug("No presence op. set available. Bailing out."); - return; - } - - //dispatch - parentOperationSet.fireSubscriptionEvent( - contact, parentGroup, SubscriptionEvent.SUBSCRIPTION_CREATED); - } - - /** - * Make the parent persistent presence operation set dispatch a contact - * resolved event. - * @param parentGroup the group that the resolved contact belongs to. - * @param contact the contact that was resolved - */ - void fireContactResolved( ContactGroup parentGroup, - ContactYahooImpl contact) - { - //bail out if no one's listening - if(parentOperationSet == null){ - if (logger.isDebugEnabled()) - logger.debug("No presence op. set available. Bailing out."); - return; - } - - //dispatch - parentOperationSet.fireSubscriptionEvent( - contact, parentGroup, SubscriptionEvent.SUBSCRIPTION_RESOLVED); - } - - /** - * When the protocol is online this method is used to fill or resolve - * the current contact list - */ - @SuppressWarnings("unchecked") //jymsg legacy code - private void initList() - { - if (logger.isTraceEnabled()) - logger.trace("Start init list of " - + yahooProvider.getAccountID().getUserID()); - - for (YahooGroup item : yahooSession.getGroups()) - { - ContactGroupYahooImpl group = findContactGroup(item.getName()); - - if(group == null) - { - // create the group as it doesn't exist - group = new ContactGroupYahooImpl( - item, item.getMembers(), this, true); - - rootGroup.addSubGroup(group); - - //tell listeners about the added group - fireGroupEvent(group, - ServerStoredGroupEvent.GROUP_CREATED_EVENT); - } - else - { - // the group exist so just resolved. The group will check and - // create or resolve its entries - group.setResolved(item); - - //fire an event saying that the group has been resolved - fireGroupEvent(group - , ServerStoredGroupEvent.GROUP_RESOLVED_EVENT); - - /** @todo if something to delete . delete it */ - } - - if (logger.isTraceEnabled()) - logger.trace("Init of group done! : " + group); - } - } - - /** - * @param name Name of the group to search - * @return The yahoo group with given name - */ - private YahooGroup findGroup(String name) - { - for (YahooGroup elem : yahooSession.getGroups()) - { - if(elem.getName().equals(name)) - return elem; - } - return null; - } - - /** - * Process incoming authorization requests. - * @param ev the event to process. - */ - void processAuthorizationRequest(SessionAuthorizationEvent ev) - { - if(handler == null) - return; - - Contact srcContact = findContactById(ev.getFrom()); - - // if there is no such contact we create it as - // volatile so we can fire notification - // and then if accepted add it in the protocol - // so we can receive its states - boolean isCurrentlyCreated = false; - if(srcContact == null) - { - srcContact = createVolatileContact(ev.getFrom()); - isCurrentlyCreated = true; - } - - AuthorizationRequest authRequest = new AuthorizationRequest(); - authRequest.setReason(ev.getMessage()); - - AuthorizationResponse authResponse = - handler.processAuthorisationRequest( - authRequest, srcContact); - - if (authResponse.getResponseCode() == AuthorizationResponse.IGNORE) - { - return; - } - else if (authResponse.getResponseCode() == AuthorizationResponse.REJECT) - { - removeContact((ContactYahooImpl)srcContact); - try - { - yahooSession.rejectFriendAuthorization( - ev, ev.getFrom(), authResponse.getReason()); - } - catch(IOException ex) - { - logger.error("cannot send auth deny", ex); - } - - return; - } - - // else we accepted it - try - { - yahooSession.acceptFriendAuthorization(ev, ev.getFrom()); - } - catch(IOException ex) - { - logger.error("cannot send auth deny", ex); - } - - if(isCurrentlyCreated) - try - { - addContact(ev.getFrom()); - } - catch (OperationFailedException ex) - { - logger.error("Cannot add friend", ex); - } - } - - /** - * Imulates firing adding contact in group and moving contact to group. - * When moving contact it is first adding to the new group then - * it is removed from the old one. - */ - private class ContactListModListenerImpl - extends SessionAdapter - { - private final Hashtable<String, Object> waitMove - = new Hashtable<String, Object>(); - - public void waitForMove(String id, String oldParent) - { - waitMove.put(id, oldParent); - } - - public void removeWaitForMove(String id) - { - waitMove.remove(id); - } - - /** - * Successfully added a friend - * friend - YahooUser of friend - * group - name of group added to - * @param ev fired event - */ - @Override - public void friendAddedReceived(SessionFriendEvent ev) - { - if (logger.isTraceEnabled()) - logger.trace("Receive event for adding a friend : " + ev); - - ContactGroupYahooImpl group = - findContactGroup(ev.getGroup()); - - if(group == null){ - if (logger.isTraceEnabled()) - logger.trace("Group not found!" + ev.getGroup()); - return; - } - - String contactID = ev.getFriend().getId(); - ContactYahooImpl contactToAdd = findContactById(contactID); - - // if group is note resolved resolve it - // this means newly created group - if(!group.isResolved()) - { - // if the contact is volatile me must remove it - // as new one will be created - if(contactToAdd != null && contactToAdd.isVolatile()) - { - ContactGroupYahooImpl parent - = (ContactGroupYahooImpl)contactToAdd - .getParentContactGroup(); - - parent.removeContact(contactToAdd); - fireContactRemoved(parent, contactToAdd); - } - - YahooGroup gr = findGroup(ev.getGroup()); - - if(gr != null) - group.setResolved(gr); - - // contact will be added when resolving the group - - return; - } - - - boolean isVolatile = false; - - if(contactToAdd == null) - { - if(addedCustomYahooIds.containsKey(contactID)) - { - String expectedContactID = - addedCustomYahooIds.remove(contactID); - - contactToAdd = - new ContactYahooImpl(expectedContactID, ev.getFriend(), - ServerStoredContactListYahooImpl.this, true, true); - } - else - { - contactToAdd = - new ContactYahooImpl(ev.getFriend(), - ServerStoredContactListYahooImpl.this, true, true); - } - } - else - { - isVolatile = contactToAdd.isVolatile(); - } - - //first check is contact is moving from a group - Object isWaitingForMove = waitMove.get(contactID); - - if(isWaitingForMove != null && isWaitingForMove instanceof String) - { - // waits for move into group - // will remove it from old group and will wait for event remove - // from group, then will fire moved to group event - String oldParent = (String)isWaitingForMove; - - group.addContact(contactToAdd); - waitMove.put(contactID, group.getSourceGroup()); - try - { - yahooSession.removeFriend(contactID, oldParent); - } - catch(IOException ex) - { - if (logger.isInfoEnabled()) - logger.info("Cannot Remove(till moving) contact :" + - contactToAdd + " from group " + oldParent); - } - return; - } - - if(isVolatile) - { - // we must remove the volatile buddy as we will add - // the persistent one. - // Volatile buddy is moving from the volatile group - // to the new one - ContactGroupYahooImpl parent = - (ContactGroupYahooImpl)contactToAdd.getParentContactGroup(); - - parent.removeContact(contactToAdd); - fireContactRemoved(parent, contactToAdd); - - contactToAdd.setPersistent(true); - contactToAdd.setResolved(ev.getFriend()); - - group.addContact(contactToAdd); - - fireContactAdded(group, contactToAdd); - waitMove.remove(contactID); - - return; - } - - group.addContact(contactToAdd); - fireContactAdded(group, contactToAdd); - } - - /** - * Successfully removed a friend - * friend - YahooUser of friend - * group - name of group removed from - * @param ev fired event - */ - @Override - public void friendRemovedReceived(SessionFriendEvent ev) - { - if (logger.isTraceEnabled()) - logger.trace("Receive event for removing a friend : " + ev); - - String contactID = ev.getFriend().getId(); - - // first check is this part of move action - Object waitForMoveObj = waitMove.get(contactID); - if(waitForMoveObj != null && waitForMoveObj instanceof YahooGroup) - { - // first get the group - oldParent - ContactGroupYahooImpl oldParent - = findContactGroup(ev.getGroup()); - ContactYahooImpl contactToRemove - = oldParent.findContact(contactID); - - oldParent.removeContact(contactToRemove); - waitMove.remove(contactID); - - ContactGroupYahooImpl newParent = - findContactGroup(((YahooGroup)waitForMoveObj).getName()); - - fireContactMoved(oldParent, newParent, contactToRemove); - return; - } - - ContactYahooImpl contactToRemove = findContactById(contactID); - - // strange we cannot find the contact to be removed - if(contactToRemove == null) - return; - - ContactGroupYahooImpl parentGroup = - (ContactGroupYahooImpl)contactToRemove. - getParentContactGroup(); - parentGroup.removeContact(contactToRemove); - fireContactRemoved(parentGroup, contactToRemove); - - // check if the group is deleted. If the contact is the last one in - // the group. The group is also deleted - if(findGroup(ev.getGroup()) == null) - { - rootGroup.removeSubGroup(parentGroup); - fireGroupEvent(parentGroup, - ServerStoredGroupEvent.GROUP_REMOVED_EVENT); - } - } - - /** - * Someone wants to add us to their friends list - * to - the target (us!) - * from - the user who wants to add us - * message - the request message text - * @param ev fired event - */ - @Override - public void contactRequestReceived(SessionEvent ev) - { - if (logger.isInfoEnabled()) - logger.info("contactRequestReceived : " + ev); - - if(handler == null || ev.getFrom() == null) - return; - - ContactYahooImpl contact = findContactById(ev.getFrom()); - - if(contact == null) - contact = createVolatileContact(ev.getFrom()); - - AuthorizationRequest request = new AuthorizationRequest(); - request.setReason(ev.getMessage()); - - AuthorizationResponse resp = - handler.processAuthorisationRequest(request, contact); - - if (resp.getResponseCode() == AuthorizationResponse.REJECT) - { - try{ - yahooSession.rejectContact(ev, resp.getReason()); - }catch(IOException ex){ - logger.error("Cannot send reject : " + ex.getMessage()); - } - } - } - - /** - * Someone has rejected our attempts to add them to our friends list - * from - the user who rejected us - * message - rejection message text - * @param ev fired event - */ - @Override - public void contactRejectionReceived(SessionEvent ev) - { - if (logger.isInfoEnabled()) - logger.info("contactRejectionReceived : " + ev); - - if(handler == null) - return; - - ContactYahooImpl contact = findContactById(ev.getFrom()); - - AuthorizationResponse resp = - new AuthorizationResponse(AuthorizationResponse.REJECT, - ev.getMessage()); - handler.processAuthorizationResponse(resp, contact); - } - - /** - * Invoked on picture received. - * @param ev fired event - */ - @Override - public void pictureReceived(SessionPictureEvent ev) - { - ContactYahooImpl contact = findContactById(ev.getFrom()); - - if(contact == null) - return; - - contact.setImage(ev.getPictureData()); - - parentOperationSet.fireContactPropertyChangeEvent( - ContactPropertyChangeEvent.PROPERTY_IMAGE, - contact, null, ev.getPictureData()); - } - - /** - * Process Authorization responses - * @param ev the event to process - */ - @Override - public void authorizationReceived(SessionAuthorizationEvent ev) - { - if(ev.isAuthorizationAccepted()) - { - if (logger.isTraceEnabled()) - logger.trace("authorizationAccepted from " + ev.getFrom()); - Contact srcContact = findContactById(ev.getFrom()); - - if(srcContact == null) - if (logger.isTraceEnabled()) - logger.trace("No contact found"); - else - handler.processAuthorizationResponse( - new AuthorizationResponse( - AuthorizationResponse.ACCEPT, - ev.getMessage()), - srcContact); - } - else if(ev.isAuthorizationDenied()) - { - if (logger.isTraceEnabled()) - logger.trace("authorizationDenied from " + ev.getFrom()); - Contact srcContact = findContactById(ev.getFrom()); - - if(srcContact == null) - if (logger.isTraceEnabled()) - logger.trace("No contact found"); - else - { - handler.processAuthorizationResponse( - new AuthorizationResponse( - AuthorizationResponse.REJECT, - ev.getMessage()), - srcContact); - try - { - removeContact((ContactYahooImpl)srcContact); - } catch (Exception ex) - { - logger.error("cannot remove denied contact : " + - srcContact, ex); - } - } - } - else if(ev.isAuthorizationRequest()) - { - if (logger.isTraceEnabled()) - logger.trace("authorizationRequestReceived from " - + ev.getFrom()); - processAuthorizationRequest(ev); - } - } - } - - /** - * Sets the yahoo session instance of the lib - * which comunicates with the server - * @param session YahooSession - */ - void setYahooSession(YahooSession session) - { - this.yahooSession = session; - session.addSessionListener(contactListModListenerImpl); - initList(); - } - - /** - * It seems that ymsg (or the Yahoo! service itself as the problem also - * appears with libpurple) would return illegal chars for names that were - * entered in cyrillic. We use this method to translate their names into - * something that we could actually display and store here. - * - * @param ymsgString the <tt>String</tt> containing illegal chars. - * - * @return a String where all illegal chars are converted into human - * readable ones - */ - static String replaceIllegalChars(String ymsgString) - { - return ymsgString.replace((char)26, '?'); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java deleted file mode 100644 index e2c589a..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * The Yahoo implementation of the Volatile ContactGroup interface. - * - * @author Damian Minkov - */ -public class VolatileContactGroupYahooImpl - extends ContactGroupYahooImpl -{ - /** - * This contact group name - */ - private String contactGroupName = null; - - /** - * Creates an Yahoo group using the specified group name - * @param groupName String groupname - * @param ssclCallback a callback to the server stored contact list - * we're creating. - */ - VolatileContactGroupYahooImpl( - String groupName, - ServerStoredContactListYahooImpl ssclCallback) - { - super(groupName, ssclCallback); - this.contactGroupName = groupName; - } - - /** - * Returns the name of this group. - * @return a String containing the name of this group. - */ - @Override - public String getGroupName() - { - return contactGroupName; - } - - /** - * Returns a string representation of this group, in the form - * YahooGroup.GroupName[size]{ buddy1.toString(), buddy2.toString(), ...}. - * @return a String representation of the object. - */ - @Override - public String toString() - { - StringBuffer buff = new StringBuffer("VolatileYahooGroup."); - buff.append(getGroupName()); - buff.append(", childContacts="+countContacts()+":["); - - Iterator<Contact> contacts = contacts(); - - while (contacts.hasNext()) - { - Contact contact = contacts.next(); - - buff.append(contact.toString()); - if(contacts.hasNext()) - buff.append(", "); - } - return buff.append("]").toString(); - } - - /** - * Determines whether or not this contact group is being stored by the - * server. Non persistent contact groups exist for the sole purpose of - * containing non persistent contacts. - * @return true if the contact group is persistent and false otherwise. - */ - @Override - public boolean isPersistent() - { - return false; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java deleted file mode 100644 index 0eb0e86..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * The Yahoo implementation of a sip-communicator AccountID - * - * @author Damian Minkov - */ -public class YahooAccountID - extends AccountID -{ - /** - * Creates an account id from the specified id and account properties. - * @param id the id identifying this account - * @param accountProperties any other properties necessary for the account. - */ - YahooAccountID(String id, Map<String, String> accountProperties ) - { - super(YahooSession.getYahooUserID(id), - accountProperties, ProtocolNames.YAHOO, "yahoo.com"); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java deleted file mode 100644 index 1e7ca01..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -import org.jitsi.service.configuration.*; -import org.jitsi.service.resources.*; -import org.osgi.framework.*; - -/** - * Loads the Yahoo provider factory and registers it with service in the OSGI - * bundle context. - * - * @author Damian Minkov - */ -public class YahooActivator - implements BundleActivator -{ - private ServiceRegistration yahooPpFactoryServReg = null; - private static BundleContext bundleContext = null; - private static ConfigurationService configurationService = null; - - private static ProtocolProviderFactoryYahooImpl yahooProviderFactory = null; - - private static ResourceManagementService resourcesService; - - /** - * Called when this bundle is started so the Framework can perform the - * bundle-specific activities necessary to start this bundle. - * - * @param context The execution context of the bundle being started. - * @throws Exception If this method throws an exception, this bundle is - * marked as stopped and the Framework will remove this bundle's - * listeners, unregister all services registered by this bundle, and - * release all services used by this bundle. - */ - public void start(BundleContext context) throws Exception - { - bundleContext = context; - - Hashtable<String, String> hashtable = new Hashtable<String, String>(); - hashtable.put(ProtocolProviderFactory.PROTOCOL, ProtocolNames.YAHOO); - - yahooProviderFactory = new ProtocolProviderFactoryYahooImpl(); - - //reg the yahoo account man. - yahooPpFactoryServReg = context.registerService( - ProtocolProviderFactory.class.getName(), - yahooProviderFactory, - hashtable); - } - - /** - * Returns a reference to a ConfigurationService implementation currently - * registered in the bundle context or null if no such implementation was - * found. - * - * @return ConfigurationService a currently valid implementation of the - * configuration service. - */ - public static ConfigurationService getConfigurationService() - { - if(configurationService == null) - { - ServiceReference confReference - = bundleContext.getServiceReference( - ConfigurationService.class.getName()); - configurationService - = (ConfigurationService) bundleContext.getService(confReference); - } - return configurationService; - } - - /** - * Returns a reference to the bundle context that we were started with. - * @return a reference to the BundleContext instance that we were started - * witn. - */ - public static BundleContext getBundleContext() - { - return bundleContext; - } - - /** - * Retrurns a reference to the protocol provider factory that we have - * registered. - * @return a reference to the <tt>ProtocolProviderFactoryYahooImpl</tt> - * instance that we have registered from this package. - */ - static ProtocolProviderFactoryYahooImpl getProtocolProviderFactory() - { - return yahooProviderFactory; - } - - /** - * Called when this bundle is stopped so the Framework can perform the - * bundle-specific activities necessary to stop the bundle. - * - * @param context The execution context of the bundle being stopped. - * @throws Exception If this method throws an exception, the bundle is - * still marked as stopped, and the Framework will remove the bundle's - * listeners, unregister all services registered by the bundle, and - * release all services used by the bundle. - */ - public void stop(BundleContext context) throws Exception - { - yahooProviderFactory.stop(); - yahooPpFactoryServReg.unregister(); - } - - public static ResourceManagementService getResources() - { - if (resourcesService == null) - { - ServiceReference serviceReference = bundleContext - .getServiceReference(ResourceManagementService.class.getName()); - - if(serviceReference == null) - return null; - - resourcesService = (ResourceManagementService) bundleContext - .getService(serviceReference); - } - - return resourcesService; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java deleted file mode 100644 index f24e50c..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.yahoo; - -import java.io.*; - -import ymsg.network.*; - -/** - * Extends The Yahoo session to have access to some - * protected functionality - * Not working for now. - * - * @author Damian Minkov - */ -public class YahooSession - extends Session -{ - /** - * Renames a group. Not working for now - */ - public void renameGroup(String oldName, String newName) - throws IOException - { - transmitGroupRename(oldName, newName); - } - - /** - * Removes the server part from the given id - */ - public static String getYahooUserID(String id) - { - return (id.indexOf("@") > -1 ) - ? id.substring(0, id.indexOf("@")) - : id; - } - - /** - * Sending typing notifications - * @param to user we are notifing - * @param from our user id - */ - void keyTyped(String to, String from) - { - try { - transmitNotify(to, from, true, " ", NOTIFY_TYPING); - }catch(IOException e){} - } - - /** - * Sending stop typing notifications - * @param to user we are notifing - * @param from our user id - */ - void stopTyping(String to, String from) - { - try { - transmitNotify(to, from, false, " ", NOTIFY_TYPING); - }catch(IOException e){} - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf deleted file mode 100644 index 39e23bf..0000000 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf +++ /dev/null @@ -1,22 +0,0 @@ -Bundle-Activator: net.java.sip.communicator.impl.protocol.yahoo.YahooActivator -Bundle-Name: Yahoo Protocol Provider Implementation -Bundle-Description: An Yahoo implementation of the Protocol Provider Service. -Bundle-Vendor: jitsi.org -Bundle-Version: 0.0.1 -Bundle-SymbolicName: net.java.sip.communicator.protocol.yahoo -Import-Package: org.osgi.framework, - javax.net.ssl, - javax.swing, - javax.swing.text, - javax.xml.parsers, - javax.naming, - javax.naming.directory, - org.xml.sax, - sun.security.action, - org.jitsi.service.configuration, - org.jitsi.service.resources, net.java.sip.communicator.service.resources, - net.java.sip.communicator.util, - net.java.sip.communicator.service.dns, - net.java.sip.communicator.service.protocol, - net.java.sip.communicator.service.protocol.yahooconstants, - net.java.sip.communicator.service.protocol.event diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/BonjourService.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/BonjourService.java deleted file mode 100644 index 8258da7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/BonjourService.java +++ /dev/null @@ -1,706 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.io.*; -import java.net.*; -import java.util.*; - -import net.java.sip.communicator.impl.protocol.zeroconf.jmdns.*; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * Class dealing with JmDNS and treating all the - * incoming connections on the bonjour port - * @author Christian Vincenot - */ -public class BonjourService extends Thread - implements ServiceListener, - DNSListener -{ - private static final Logger logger = - Logger.getLogger(BonjourService.class); - - private int port = 5298; - private ServerSocket sock = null; - private String id = null; - private JmDNS jmdns=null; - private final Map<String, Object> props = new Hashtable<String, Object>(); - private ServiceInfo service = null; - private boolean dead = false; - - private final List<ContactZeroconfImpl> contacts - = new Vector<ContactZeroconfImpl>(); - - private ProtocolProviderServiceZeroconfImpl pps; - OperationSetPersistentPresenceZeroconfImpl opSetPersPresence; - - private ZeroconfAccountID acc; - - /* Should maybe better get the status directly from OperationSetPresence */ - private PresenceStatus status = ZeroconfStatusEnum.OFFLINE; - - /** - * Returns the corresponding ProtocolProviderService - * @return corresponding ProtocolProviderService - */ - public ProtocolProviderServiceZeroconfImpl getPPS() - { - return pps; - } - - /** - * Returns the id of this service. - * @return returns the id of this service. - */ - String getID() - { - return id; - } - - /** - * Creates a new instance of the Bonjour service thread - * @param port TCP Port number on which to try to start the Bonjour service - * @param pps ProtocolProviderService instance - * which is creating this BonjourService - */ - public BonjourService(int port, - ProtocolProviderServiceZeroconfImpl pps) - { - this.acc = (ZeroconfAccountID) pps.getAccountID(); - this.port = port; - this.id = acc.getUserID(); - this.pps = pps; - - opSetPersPresence = - (OperationSetPersistentPresenceZeroconfImpl) pps - .getOperationSet(OperationSetPersistentPresence.class); - - // Gaim - props.put("1st", (acc.getFirst() == null)? "":acc.getFirst()); - props.put("email", (acc.getMail() == null)? "":acc.getMail()); - props.put("jid", this.id); - props.put("last", (acc.getLast() == null)?"":acc.getLast()); - props.put("msg", opSetPersPresence.getCurrentStatusMessage()); - props.put("status", "avail"); - - //iChat - props.put("phsh","000"); - //props.put("status","avail"); - //props.put("port.p2pj", "5298"); - props.put("vc", "C!"); - //props.put("1st", "John"); - props.put("txtvers","1"); - - //XEP-0174 (Final paper) - props.put("ext",""); - props.put("nick", (acc.getFirst() == null)? this.id:acc.getFirst()); - props.put("ver", "1"); - props.put("node", "SIP Communicator"); - - //Ours - props.put("client", "SIP Communicator"); - - changeStatus(opSetPersPresence.getPresenceStatus()); - - sock = createSocket(port); - if (sock == null) - return; - - port = sock.getLocalPort(); - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: ServerSocket bound to port "+port); - - props.put("port.p2pj", Integer.toString(port)); - this.setDaemon(true); - this.start(); - } - - /* TODO: Better exception checking to avoid sudden exit and bonjour - * service shutdown */ - - /** - * Walk? - */ - @Override - public void run() - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Bonjour Service Thread up and running!"); - - /* Put jmDNS in DEBUD Mode : - * Following verbosity levels can be chosen : - * "INFO" , "WARNING", "SEVERE", "ALL", "FINE", "FINER", "FINEST", etc - */ - //System.setProperty("jmdns.debug", "0"); - - while (dead == false) - { - if (sock == null || sock.isClosed()) - { - sock = createSocket(port); - /* What should we do now? TEMPORARY: shutdown()*/ - if (sock == null) shutdown(); - port = sock.getLocalPort(); - props.put("port.p2pj", Integer.toString(port)); - //TODO: update JmDNS in case the port had to be changed! - } - try - { - Socket connection = sock.accept(); - ContactZeroconfImpl contact = getContact(null, - connection.getInetAddress()); - /*if (status.equals(ZeroconfStatusEnum.OFFLINE) - || status.equals(ZeroconfStatusEnum.INVISIBLE) */ - if (dead == true) break; - - if ((contact == null) - || (contact.getClientThread() != null)) - { - if (contact == null) - logger.error("ZEROCONF: Connexion from " - + "unknown contact [" - + connection.getInetAddress() - +"]. REJECTING!"); - else if (contact.getClientThread() == null) - logger.error("ZEROCONF: Redundant chat " - + "channel [" - + contact - +"]. REJECTING!"); - connection.close(); - } - else new ClientThread(connection, this); - } - catch(Exception e) - { - logger.error(e); - } - } - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Going Offline - " - +"BonjourService Thread exiting!"); - } - - /** - * Might be used for shutdown... - */ - public void shutdown() - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Shutdown!"); - - dead = true; - try - { sock.close(); } - catch (Exception ex) - { logger.error(ex); } - - changeStatus(ZeroconfStatusEnum.OFFLINE); - if(jmdns != null) - jmdns.close(); - } - - private ServerSocket createSocket(int port) - { - ServerSocket sock=null; - try - { - sock = new ServerSocket(port); - } - catch(Exception e) - { - logger.error("ZEROCONF: Couldn't bind socket to port " - +port+"! Switching to an other port..."); - try - { - sock = new ServerSocket(0); - } - catch (IOException ex) - { - logger.error("ZEROCONF: FATAL ERROR => " - +"Couldn't bind to a port!!", ex); - } - } - - return sock; - } - - /** - * Changes the status of the local user. - * @param stat New presence status - */ - public void changeStatus(PresenceStatus stat) - { - /* [old_status == new_status ?] => NOP */ - if (stat.equals(status)) - return; - - /* [new_status == OFFLINE ?] => clean up everything */ - if (stat.equals(ZeroconfStatusEnum.OFFLINE)) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Going OFFLINE"); - //jmdns.unregisterAllServices(); - jmdns.removeServiceListener("_presence._tcp.local.", this); - jmdns.close(); - jmdns=null; - //dead = true; - - // Erase all contacts by putting them OFFLINE - opSetPersPresence.changePresenceStatusForAllContacts( - opSetPersPresence.getServerStoredContactListRoot(), stat); - - try - { - sleep(1000); - } catch (InterruptedException ex) - { - logger.error(ex); - } - } - - /* [old_status == OFFLINE ?] => register service */ - else if (status.equals(ZeroconfStatusEnum.OFFLINE)) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Getting out of OFFLINE state"); - props.put("status", stat.getStatusName()); - service = new ServiceInfo("_presence._tcp.local.", id, - port, 0, 0, props); - - try - { - jmdns = new JmDNS(); - jmdns.registerServiceType("_presence._tcp.local."); - jmdns.addServiceListener("_presence._tcp.local.", this); - jmdns.registerService(service); - - /* In case the ID had to be changed */ - id = service.getName(); - } - catch (Exception ex) - { logger.error(ex); } - - //dead = false; - - /* Normal status change */ - } - else - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF : Changing status"); - - props.put("status", stat.getStatusName()); - - /* FIXME: Not totally race condition free since the 3 calls aren't - * atomic, but that's not really critical since there's little - * change chance of concurrent local contact change, and this - * wouldn't have big consequences. - */ - ServiceInfo info = - jmdns.getLocalService(id.toLowerCase()+"._presence._tcp.local."); - if (info == null) - logger.error("ZEROCONF/JMDNS: PROBLEM GETTING " - +"LOCAL SERVICEINFO !!"); - - byte[] old = info.getTextBytes(); - info.setProps(props); - jmdns.updateInfos(info, old); - } - - status = stat; - } - - private class AddThread extends Thread - { - private String type, name; - public AddThread(String type, String name) - { - this.setDaemon(true); - this.type = type; - this.name = name; - this.start(); - } - - @Override - public void run() - { - ServiceInfo service = null; - while ((service == null) && (dead == false) - && !status.equals(ZeroconfStatusEnum.OFFLINE)) - { - service = jmdns.getServiceInfo(type, name, 10000); - if (service == null) - logger.error("BONJOUR: ERROR - Service Info of " - + name +" not found in cache!!"); - try - { - sleep(2); - } - catch (InterruptedException ex) - { - logger.error(ex); - } - } - if ((dead == false) && !status.equals(ZeroconfStatusEnum.OFFLINE)) - jmdns.requestServiceInfo(type, name); - //} else handleResolvedService(name, type, service); - } - } - - /* Service Listener Implementation */ - - /** - * A service has been added. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - public void serviceAdded(ServiceEvent event) - { - /* WARNING: DONT PUT ANY BLOCKING CALLS OR FLAWED LOOPS IN THIS METHOD. - * JmDNS calls this method without creating a new thread, so if this - * method doesn't return, jmDNS will hang !! - */ - - String name = event.getName(); - String type = event.getType(); - - if (name.equals(id)) - return; - - if (logger.isDebugEnabled()) - logger.debug("BONJOUR: "+name - +"["+type+"] detected! Trying to get information..."); - try - { - sleep(2); - } - catch (InterruptedException ex) - { - logger.error(ex); - } - - jmdns.printServices(); - - new AddThread(type, name); - } - - - - /** - * A service has been removed. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - public void serviceRemoved(ServiceEvent event) - { - String name = event.getName(); - if (name.equals(id)) - return; - - ContactZeroconfImpl contact = getContact(name, null); - - if(contact == null) - return; - - opSetPersPresence.changePresenceStatusForContact(contact, - ZeroconfStatusEnum.OFFLINE); - if (logger.isDebugEnabled()) - logger.debug("BONJOUR: Received announcement that " - +name+" went offline!"); - - } - - /** - * A service has been resolved. Its details are now available in the - * ServiceInfo record. - * - * @param event The ServiceEvent providing the name, the fully qualified - * type of the service, and the service info record, - * or null if the service could not be resolved. - */ - public void serviceResolved(ServiceEvent event) - { - String contactID = event.getName(); - String type = event.getType(); - ServiceInfo info = event.getInfo(); - - if (logger.isDebugEnabled()) - logger.debug("BONJOUR: Information about " - +contactID+" discovered"); - - handleResolvedService(contactID, type, info); - } - - private void handleResolvedService(String contactID, - String type, - ServiceInfo info) - { - if (contactID.equals(id)) - return; - - if (info.getAddress().toString().length() > 15) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Temporarily ignoring IPv6 addresses!"); - return; - } - - ContactZeroconfImpl newFriend; - - synchronized(this) - { - if (getContact(contactID, info.getAddress()) != null) - { - if (logger.isDebugEnabled()) - logger.debug("Contact " - +contactID+" already in contact list! Skipping."); - return; - }; - if (logger.isDebugEnabled()) - logger.debug("ZEROCNF: ContactID " + contactID + - " Address " + info.getAddress()); - - if (logger.isDebugEnabled()) - logger.debug(" Address=>"+info.getAddress() - +":"+info.getPort()); - - for (Iterator<String> names = info.getPropertyNames(); - names.hasNext();) - { - String prop = names.next(); - if (logger.isDebugEnabled()) - logger.debug(" "+prop+"=>" - +info.getPropertyString(prop)); - } - - /* Creating the contact */ - String name = info.getPropertyString("1st"); - if (info.getPropertyString("last") != null) - name += " "+ info.getPropertyString("last"); - - int port = Integer.valueOf( - info.getPropertyString("port.p2pj")).intValue(); - - if (port < 1) - { - logger.error("ZEROCONF: Flawed contact announced himself" - +"without necessary parameters : "+contactID); - return; - } - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Detected client "+name); - - newFriend = - opSetPersPresence.createVolatileContact( - contactID, this, name, - info.getAddress(), port); - } - /* Try to detect which client type it is */ - int clientType = ContactZeroconfImpl.XMPP; - if (info.getPropertyString("client") != null - && info.getPropertyString("client"). - compareToIgnoreCase("SIP Communicator") == 0) - clientType = ContactZeroconfImpl.SIPCOM; - - else if ((info.getPropertyString("jid") != null) - && (info.getPropertyString("node") == null)) - clientType = ContactZeroconfImpl.GAIM; - - else if (info.getPropertyString("jid") == null) - clientType = ContactZeroconfImpl.ICHAT; - - newFriend.setClientType(clientType); - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: CLIENT TYPE "+clientType); - - ZeroconfStatusEnum status = - ZeroconfStatusEnum.statusOf(info.getPropertyString("status")); - opSetPersPresence. - changePresenceStatusForContact(newFriend, - status == null?ZeroconfStatusEnum.ONLINE:status); - - // Listening for changes - jmdns.addListener(this, new DNSQuestion(info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_UNIQUE)); - } - - /** - * Callback called by JmDNS to inform the - * BonjourService of a potential status change of some contacts. - * @param jmdns JmDNS instance responsible for this - * @param now Timestamp - * @param record DNSRecord which changed - */ - public synchronized void updateRecord( JmDNS jmdns, - long now, - DNSRecord record) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF/JMDNS: Received record update for "+record); - - int clazz = record.getClazz(); - int type = record.getType(); - - /* Check the info returned by JmDNS since we can't really trust its - * filtering. */ - if (!(((type & DNSConstants.TYPE_TXT) != 0) && - ((clazz & DNSConstants.CLASS_IN) != 0) && - record.isUnique() && - record.getName().endsWith("_presence._tcp.local."))) - return; - - String name = record.getName().replaceAll("._presence._tcp.local.",""); - ContactZeroconfImpl contact; - - synchronized(this) - { - contact = getContact(name, null); - - if (contact == null) { //return; - logger.error("ZEROCONF: BUG in jmDNS => Received update without " - +"previous contact annoucement. Trying to add contact"); - new AddThread("_presence._tcp.local.", name); - return; - } - } - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: "+ name - + " changed status. Requesting fresh data!"); - - /* Since a record was updated, we can be sure that we can do a blocking - * getServiceInfo without risk. (Still, we use the method with timeout - * to avoid bad surprises). If some problems of status change refresh - * appear, we'll have to fall back on the method with callback as we've - * done for "ServiceAdded". - */ - - ServiceInfo info = jmdns.getServiceInfo("_presence._tcp.local.", name, - 1000); - if (info == null) - { - logger.error("ZEROCONF/JMDNS: Problem!! The service " - +"information was not in cache. See comment in " - +"BonjourService.java:updateRecord !!"); - return; - } - - /* Let's change what we can : status, message, etc */ - ZeroconfStatusEnum status = - ZeroconfStatusEnum.statusOf(info.getPropertyString("status")); - - opSetPersPresence. - changePresenceStatusForContact(contact, - status == null ? ZeroconfStatusEnum.ONLINE:status); - - } - - /** - * Returns an Iterator over all contacts. - * - * @return a java.util.Iterator over all contacts - */ - public Iterator<ContactZeroconfImpl> contacts() - { - return contacts.iterator(); - } - - /** - * Adds a contact to the locally stored list of contacts - * @param contact Zeroconf Contact to add to the local list - */ - public void addContact(ContactZeroconfImpl contact) - { - if (contact == null) - throw new IllegalArgumentException("contact"); - - synchronized(contacts) - { - contacts.add(contact); - } - } - /** - * Returns the <tt>Contact</tt> with the specified identifier or IP address. - * - * @param id the identifier of the <tt>Contact</tt> we are - * looking for. - * @param ip the IP address of the <tt>Contact</tt> we are looking for. - * @return the <tt>Contact</tt> with the specified id or address. - */ - public ContactZeroconfImpl getContact(String id, InetAddress ip) - { - if (id == null && ip == null) return null; - - synchronized(contacts) - { - Iterator<ContactZeroconfImpl> contactsIter = contacts(); - - while (contactsIter.hasNext()) - { - ContactZeroconfImpl contact = contactsIter.next(); - //System.out.println("ZEROCNF: Comparing "+id+ " "+ip+ - //" with "+ contact.getAddress()+ " " + contact.getIpAddress()); - if (((contact.getAddress().equals(id)) || (id == null)) - && ((contact.getIpAddress().equals(ip)) || (ip == null))) - return contact; - - } - } - //System.out.println("ZEROCNF: ERROR - " + - //"Couldn't find contact to get ["+id+" / "+ip+"]"); - return null; - } - - /** - * Removes the <tt>Contact</tt> with the specified identifier or IP address. - * - * - * @param id the identifier of the <tt>Contact</tt> we are - * looking for. - * @param ip the IP address of the <tt>Contact</tt> we are looking for. - */ - public void removeContact(String id, InetAddress ip) - { - synchronized(contacts) - { - Iterator<ContactZeroconfImpl> contactsIter = contacts(); - while (contactsIter.hasNext()) - { - ContactZeroconfImpl contact = contactsIter.next(); - if (((contact.getAddress().equals(id)) || (id == null)) - &&((contact.getIpAddress().equals(ip)) || (ip == null))) - { - if (contact.getClientThread() != null) - contact.getClientThread().cleanThread(); - contacts.remove(contact); - return; - } - }; - } - logger.error( - "ZEROCONF: ERROR - Couldn't find contact to delete ["+id+" / "+ip+"]"); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ClientThread.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ClientThread.java deleted file mode 100644 index 4367eed..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ClientThread.java +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.io.*; -import java.net.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * Class creating a thread responsible for handling the chat - * with the remote user on the other end of the socket - * - * @author Christian Vincenot - */ -public class ClientThread - extends Thread -{ - private static final Logger logger = Logger.getLogger(ClientThread.class); - - private OperationSetBasicInstantMessagingZeroconfImpl opSetBasicIM; - private OperationSetTypingNotificationsZeroconfImpl opSetTyping; - private Socket sock; - private InetAddress remoteIPAddress; - private OutputStream out; - private DataInputStream in; - private BonjourService bonjourService; - private ContactZeroconfImpl contact=null; - private boolean streamState = false; - - private String messagesQueue=null; - - /** - * Sets the contact with which we're chatting in this ClientThread - * @param contact Zeroconf contact with which we're chatting - */ - protected void setContact(ContactZeroconfImpl contact) - { - this.contact = contact; - } - - /** - * Set the stream as opened. This means that the - * conversation with the client is really opened - * from now on (the XML greetings are over) - */ - protected void setStreamOpen() - { - synchronized(this) - { - this.streamState = true; - } - } - - /** - * Says if the stream between the local user and the remote user - * is in an opened state (greetings are over and we can chat) - * @return Returns true if the stream is "opened" (ie, ready for chat) - */ - protected boolean isStreamOpened() - { - synchronized(this) - { - return this.streamState; - } - } - - /** - * Creates a new instance of ClientThread reponsible - * for handling the conversation with the remote user. - * @param sock Socket created for chatting - * @param bonjourService BonjourService which spawned this ClientThread - */ - public ClientThread(Socket sock, BonjourService bonjourService) - { - this.sock = sock; - this.remoteIPAddress = sock.getInetAddress(); - this.bonjourService = bonjourService; - this.opSetBasicIM = - (OperationSetBasicInstantMessagingZeroconfImpl) bonjourService - .getPPS().getOperationSet( - OperationSetBasicInstantMessaging.class); - - this.opSetTyping = - (OperationSetTypingNotificationsZeroconfImpl) bonjourService - .getPPS() - .getOperationSet(OperationSetTypingNotifications.class); - this.setDaemon(true); - - try - { - out = sock.getOutputStream(); - in = new DataInputStream(sock.getInputStream()); - } - catch (IOException e) - { - logger.error("Creating ClientThread: Couldn't get I/O for " - +"the connection", e); - //System.exit(1); - return; - } - - this.start(); - } - - /* - * Read a message from the socket. - * TODO: clean the code a bit and optimize it. - */ - private String readMessage() - { - String line; - byte[] bytes = new byte[10]; - - try - { - int i=0; - - while (i < 9) - { - i += in.read(bytes,0,9-i); - } - - line = new String(bytes); - bytes = new byte[1]; - if ((line.getBytes())[0] == '\n') - line = line.substring(1); - - if (line.startsWith("<message")) - { - while (true) - { - bytes[0] = in.readByte(); - line += new String(bytes); - - if ((line.endsWith("</message>")) - || (line.endsWith("stream>"))) - return line; - } - } - else - { - while (true) - { - bytes[0] = in.readByte(); - line += new String(bytes); - if ( ">".compareTo(new String(bytes)) == 0 ) - return line; - } - } - } - catch (IOException e) - { - logger.error("Couldn't get I/O for the connection", e); - //System.exit(1); - } - - return null; - } - - /* - * Parse the payload and extract the information. - * TODO: If needed, fill in the remaining fields of MessageZeroconfImpl - * like the baloon icon color, color/size/font of the text. - */ - private MessageZeroconfImpl parseMessage(String str) - { - if (str.startsWith("<?xml") || str.startsWith("<stream")) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.STREAM_OPEN); - - if (str.endsWith("stream>")) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.STREAM_CLOSE); - - if ((str.indexOf("<delivered/>") > 0) && (str.indexOf("<body>") < 0)) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.DELIVERED); - - if (!str.startsWith("<message")) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.UNDEF); - - /* TODO: Parse Enconding (& contact id to be able to double-check - * the source of a message) - * - * TODO: Check that the fields are outside of <body>..</body> - */ - - if ((str.indexOf("<body>") < 0) || (str.indexOf("</body>") < 0)) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.UNDEF); - - String temp = - str.substring(str.indexOf("<body>")+6, str.indexOf("</body>")); - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: received message ["+temp+"]"); - - int messageType = MessageZeroconfImpl.MESSAGE; - - if ((str.indexOf("<id>") >= 0) && (str.indexOf("</id>") >= 0)) - messageType = MessageZeroconfImpl.TYPING; - - MessageZeroconfImpl msg = - new MessageZeroconfImpl( - temp, - null, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE, - messageType); - - return msg; - } - - private int handleMessage(MessageZeroconfImpl msg) - { - - switch(msg.getType()) - { - /* STREAM INIT */ - case MessageZeroconfImpl.STREAM_OPEN: - if (contact == null) - contact = bonjourService.getContact(null, remoteIPAddress); - if (!isStreamOpened()) - { - sendHello(); - setStreamOpen(); - } - if (messagesQueue != null) - { - write(messagesQueue); - messagesQueue = null; - } - break; - - /* ACK */ - case MessageZeroconfImpl.DELIVERED : break; - - /* NORMAL MESSAGE */ - case MessageZeroconfImpl.MESSAGE: - if (!isStreamOpened()) - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: client on the other side " - +"isn't polite (sending messages without " - +"saying hello :P"); - if (contact == null) - //TODO: Parse contact id to double-check - contact = bonjourService.getContact(null, remoteIPAddress); - - /* TODO: If we want to implement invisible status, we'll have to - * make this test less restrictive to handle messages from - * unannounced clients. - */ - if (contact == null) - { - logger.error("ZEROCONF: ERROR - Couldn't identify " - +"contact. Closing socket."); - return -1; - } - else if (contact.getClientThread() == null) - contact.setClientThread(this); - - opSetBasicIM.fireMessageReceived(msg, contact); - - opSetTyping.fireTypingNotificationsEvent(contact, - OperationSetTypingNotificationsZeroconfImpl.STATE_STOPPED); - break; - - case MessageZeroconfImpl.TYPING: - if (!isStreamOpened()) - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: client on the other side " - +"isn't polite (sending messages without " - +"saying hello :P"); - if (contact == null) - //TODO: Parse contact id to double-check - contact = bonjourService.getContact(null, remoteIPAddress); - opSetTyping.fireTypingNotificationsEvent(contact, - OperationSetTypingNotificationsZeroconfImpl.STATE_TYPING); - - /* TODO: code a private runnable class to be used as timeout - * to set the typing state to STATE_PAUSED when a few seconds - * without news have passed. - */ - - break; - - case MessageZeroconfImpl.STREAM_CLOSE: - sendBye(); - contact.setClientThread(null); - return 1; - - case MessageZeroconfImpl.UNDEF: - logger.error("ZEROCONF: received strange message. SKIPPING!"); - break; - } - - //System.out.println("RECEIVED MESSAGE "+ msg.getContent()+ - //" from "+contact.getAddress() + "!!!!!!!!!!!!!!"); - return 0; - } - - - private void write(String string) - { - //System.out.println("Writing " + string + "!!!!!!!!!"); - byte[] bytes = string.getBytes(); - try - { - out.write(bytes); - out.flush(); - } - catch (IOException e) - { - logger.error("Couldn't get I/O for the connection"); - if (contact != null) - { - contact.setClientThread(null); - } - - try - { - sock.close(); - } - catch (IOException ex) - { - logger.error(ex); - } - - } - } - - /** - * Say hello :) - */ - protected void sendHello() - { - switch(contact.getClientType()) - { - case ContactZeroconfImpl.GAIM: - case ContactZeroconfImpl.ICHAT: - case ContactZeroconfImpl.SIPCOM: - write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); - write("<stream:stream xmlns=\"jabber:client\" " - +"xmlns:stream=\"http://etherx.jabber.org/streams\">"); - break; - case ContactZeroconfImpl.XMPP: - write("<stream:stream" - +"xmlns='jabber:client'" - +"xmlns:stream='http://etherx.jabber.org/streams'" - +"from='"+bonjourService.getID()+"'" - +"to='"+contact.getAddress()+"'" - +"version='1.0'>\n"); - break; - } - - /* Legacy: OLD XMPP (XEP-0174 Draft) */ - //write("<stream:stream to='"+sock.getInetAddress().getHostAddress() - //+"' xmlns='jabber:client' stream='http://etherx.jabber.org/streams'>"); - } - - private void sendBye() - { - write("</stream:stream>\n"); - } - - private String toXHTML(MessageZeroconfImpl msg) - { - switch(contact.getClientType()) - { - case ContactZeroconfImpl.XMPP: - return new String("<message to='" - +contact.getAddress()+"' from='" - +bonjourService.getID()+"'>" - + "<body>"+msg.getContent()+"</body>" - + "</message>\n"); - - case ContactZeroconfImpl.SIPCOM: - - case ContactZeroconfImpl.ICHAT: - return new String( - "<message to='"+sock.getInetAddress().getHostAddress() - +"' type='chat' id='"+bonjourService.getID()+"'>" - + "<body>"+msg.getContent()+"</body>" - + "<html xmlns='http://www.w3.org/1999/xhtml'>" - + "<body ichatballooncolor='#7BB5EE' " - + "ichattextcolor='#000000'>" - + "<font face='Helvetica' ABSZ='12' color='#000000'>" - + msg.getContent() - + "</font>" - + "</body>" - + "</html>" - + "<x xmlns='jabber:x:event'>" - + "<offline/>" - + "<delivered/>" - + "<composing/>" - + (msg.getType()==MessageZeroconfImpl.TYPING?"<id></id>":"") - + "</x>" - + "</message>"); - - case ContactZeroconfImpl.GAIM: - default: - return new String( - "<message to='"+contact.getAddress() - +"' from='"+bonjourService.getID() - + "' type='chat'><body>"+msg.getContent()+"</body>" - + "<html xmlns='http://www.w3.org/1999/xhtml'><body><font>" - + msg.getContent() - + "</font></body></html><x xmlns='jabber:x:event'><composing/>" - + (msg.getType()==MessageZeroconfImpl.TYPING?"<id></id>":"") - + "</x></message>\n"); - } - } - - - /** - * Send a message to the remote user - * @param msg Message to send - */ - public void sendMessage(MessageZeroconfImpl msg) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Sending messag [" - +msg.getContent()+"] to " - + contact.getDisplayName()); - if (!isStreamOpened()) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Stream not opened... " - +"will send the message later"); - messagesQueue += toXHTML(msg); - } - else write(toXHTML(msg)); - } - - /** - * Walk? - */ - @Override - public void run() - { - if (logger.isDebugEnabled()) - logger.debug("Bonjour: NEW CONNEXION from " - + sock.getInetAddress().getCanonicalHostName() - +" / "+sock.getInetAddress().getHostAddress()); - String input; - MessageZeroconfImpl msg=null; - - - input = readMessage(); - msg = parseMessage(input); - - while (handleMessage(msg) == 0 && !sock.isClosed()) - { - input = readMessage(); - msg = parseMessage(input); - } - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF : OUT OF LOOP !! Closed chat."); - cleanThread(); - } - - /** - * Clean-up the thread to exit - */ - public void cleanThread() - { - /* I wonder if that's ok... */ - if (sock != null && sock.isClosed() == false) - { - sendBye(); - try - { - sock.close(); - } - catch (IOException ex) - { - logger.error(ex); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactGroupZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactGroupZeroconfImpl.java deleted file mode 100644 index 568e087..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactGroupZeroconfImpl.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * A simple, straightforward implementation of a zeroconf ContactGroup. Since - * the Zeroconf protocol, 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 Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - */ -public class ContactGroupZeroconfImpl - implements ContactGroup -{ - - /** - * The name of this Zeroconf contact group. - */ - private String groupName = null; - - /** - * The list of this group's members. - */ - private Vector<Contact> contacts = new Vector<Contact>(); - - /** - * The list of sub groups belonging to this group. - */ - private Vector<ContactGroup> subGroups = new Vector<ContactGroup>(); - - /** - * The group that this group belongs to (or null if this is the root group). - */ - private ContactGroupZeroconfImpl 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 = false; - - /** - * The protocol provider that created us. - */ - private ProtocolProviderServiceZeroconfImpl 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 ContactGroupZeroconfImpl with the specified name. - * - * @param groupName the name of the group. - * @param parentProvider the protocol provider that created this group. - */ - public ContactGroupZeroconfImpl( - String groupName, - ProtocolProviderServiceZeroconfImpl 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<Contact> contacts() - { - return contacts.iterator(); - } - - /** - * Adds the specified contact to this group. - * @param contactToAdd the ContactZeroconfImpl to add to this group. - */ - public void addContact(ContactZeroconfImpl 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 ContactGroupZeroconfImpl to add as a - * subgroup to this group. - */ - public void addSubgroup(ContactGroupZeroconfImpl subgroup) - { - this.subGroups.add(subgroup); - subgroup.setParentGroup(this); - } - - /** - * Sets the group that is the new parent of this group - * @param parent ContactGroupZeroconfImpl - */ - void setParentGroup(ContactGroupZeroconfImpl 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 ContactGroupZeroconfImpl subgroup to remove. - */ - public void removeSubGroup(ContactGroupZeroconfImpl subgroup) - { - this.subGroups.remove(subgroup); - subgroup.setParentGroup(null); - } - - /** - * Returns the group that is parent of the specified zeroconfGroup or null - * if no parent was found. - * @param zeroconfGroup the group whose parent we're looking for. - * @return the ContactGroupZeroconfImpl instance that zeroconfGroup - * belongs to or null if no parent was found. - */ - public ContactGroupZeroconfImpl findGroupParent( - ContactGroupZeroconfImpl zeroconfGroup) - { - if ( subGroups.contains(zeroconfGroup) ) - return this; - - Iterator<ContactGroup> subGroupsIter = subgroups(); - while (subGroupsIter.hasNext()) - { - ContactGroupZeroconfImpl subgroup - = (ContactGroupZeroconfImpl) subGroupsIter.next(); - - ContactGroupZeroconfImpl parent - = subgroup.findGroupParent(zeroconfGroup); - - if(parent != null) - return parent; - } - return null; - } - - /** - * Returns the group that is parent of the specified zeroconfContact or - * null if no parent was found. - * - * @param zeroconfContact the contact whose parent we're looking for. - * @return the ContactGroupZeroconfImpl instance that zeroconfContact - * belongs to or <tt>null</tt> if no parent was found. - */ - public ContactGroupZeroconfImpl findContactParent( - ContactZeroconfImpl zeroconfContact) - { - if ( contacts.contains(zeroconfContact) ) - return this; - - Iterator<ContactGroup> subGroupsIter = subgroups(); - while (subGroupsIter.hasNext()) - { - ContactGroupZeroconfImpl subgroup - = (ContactGroupZeroconfImpl) subGroupsIter.next(); - - ContactGroupZeroconfImpl parent - = subgroup.findContactParent(zeroconfContact); - - 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<Contact> contactsIter = contacts(); - while (contactsIter.hasNext()) - { - ContactZeroconfImpl contact = - (ContactZeroconfImpl)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 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<ContactGroup> groupsIter = subgroups(); - while (groupsIter.hasNext()) - { - ContactGroupZeroconfImpl contactGroup - = (ContactGroupZeroconfImpl) 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<ContactGroup> subgroups() - { - return subGroups.iterator(); - } - - /** - * Removes the specified contact from this group. - * @param contact the ContactZeroconfImpl to remove from this group - */ - public void removeContact(ContactZeroconfImpl 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 ContactZeroconfImpl - */ - public ContactZeroconfImpl findContactByID(String id) - { - //first go through the contacts that are direct children. - Iterator<Contact> contactsIter = contacts(); - - while(contactsIter.hasNext()) - { - ContactZeroconfImpl mContact = - (ContactZeroconfImpl)contactsIter.next(); - - if( mContact.getAddress().equals(id) ) - return mContact; - } - - //if we didn't find it here, let's try in the subougroups - Iterator<ContactGroup> groupsIter = subgroups(); - - while( groupsIter.hasNext() ) - { - ContactGroupZeroconfImpl mGroup = - (ContactGroupZeroconfImpl)groupsIter.next(); - - ContactZeroconfImpl 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. - */ - @Override - public String toString() - { - - StringBuffer buff = new StringBuffer(getGroupName()); - buff.append(".subGroups=" + countSubgroups() + ":\n"); - - Iterator<ContactGroup> subGroups = subgroups(); - while (subGroups.hasNext()) - { - ContactGroupZeroconfImpl group = - (ContactGroupZeroconfImpl)subGroups.next(); - buff.append(group.toString()); - if (subGroups.hasNext()) - buff.append("\n"); - } - - buff.append("\nChildContacts="+countContacts()+":["); - - Iterator<Contact> contacts = contacts(); - while (contacts.hasNext()) - { - ContactZeroconfImpl contact = (ContactZeroconfImpl) 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. - */ - @Override - public boolean equals(Object obj) - { - if(obj == null - || !(obj instanceof ContactGroupZeroconfImpl)) - return false; - - ContactGroupZeroconfImpl zeroconfGroup - = (ContactGroupZeroconfImpl)obj; - - if(!zeroconfGroup.getGroupName().equals(getGroupName()) || - !zeroconfGroup.getUID().equals(getUID()) || - zeroconfGroup.countContacts() != countContacts() || - zeroconfGroup.countSubgroups() != countSubgroups()) - return false; - - //traverse child contacts - Iterator<Contact> theirContacts = zeroconfGroup.contacts(); - - while(theirContacts.hasNext()) - { - ContactZeroconfImpl theirContact - = (ContactZeroconfImpl)theirContacts.next(); - - ContactZeroconfImpl ourContact - = (ContactZeroconfImpl)getContact(theirContact.getAddress()); - - if(ourContact == null - || !ourContact.equals(theirContact)) - return false; - } - - //traverse subgroups - Iterator<ContactGroup> theirSubgroups = zeroconfGroup.subgroups(); - - while(theirSubgroups.hasNext()) - { - ContactGroupZeroconfImpl theirSubgroup - = (ContactGroupZeroconfImpl)theirSubgroups.next(); - - ContactGroupZeroconfImpl ourSubgroup - = (ContactGroupZeroconfImpl)getGroup( - theirSubgroup.getGroupName()); - - if(ourSubgroup == null - || !ourSubgroup.equals(theirSubgroup)) - return false; - } - - return true; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactZeroconfImpl.java deleted file mode 100644 index c22cf29..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactZeroconfImpl.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.net.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * A simple, straightforward implementation of a zeroconf Contact. Since - * the Zeroconf protocol is not a real one, we simply store all contact details - * in class fields. You should know that when implementing a real protocol, - * the contact implementation would rather encapsulate contact objects from - * the protocol stack and group property values should be returned after - * consulting the encapsulated object. - * - * @author Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - */ -public class ContactZeroconfImpl - extends AbstractContact -{ - private static final Logger logger - = Logger.getLogger(ContactZeroconfImpl.class); - - - /** - * The id of the contact. - */ - private String contactID = null; - - /** - * The ClientThread attached to this contact if we're already chatting - * with him. - */ - private ClientThread thread = null; - - /* - * Type of Client. - */ - /** - * Gaim/Pidgin client type - */ - public static final int GAIM = 1; - /** - * iChat client type - */ - public static final int ICHAT = 2; - /** - * XMPP - XEP-0174 client type - */ - public static final int XMPP = 3; - /** - * Another SIP Communicator client - */ - public static final int SIPCOM = 4; - private int clientType = XMPP; - - - /** - * The provider that created us. - */ - private ProtocolProviderServiceZeroconfImpl parentProvider = null; - - - /* - * The Bonjour Service who discovered this contact. - * TODO: This could probably be avoided using only the - * Protocol Provider. - */ - private BonjourService bonjourService; - - /** - * The group that belong to. - */ - private ContactGroupZeroconfImpl parentGroup = null; - - /** - * The presence status of the contact. - */ - private PresenceStatus presenceStatus = ZeroconfStatusEnum.OFFLINE; - - /** - * Determines whether this contact is persistent, - * i.e. member of the contact list or whether it is here only temporarily. - * Chris: should be set to false here - */ - private boolean isPersistent = false; - - /** - * 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; - - /** - * IP Address - */ - private InetAddress ipAddress; - - /** - * Port on which Bonjour is listening. - */ - private int port; - - /** - * Name announced by Bonjour. - */ - private String name; - - /** - * Contact personal message - */ - private String message; - - - /** - * Creates an instance of a meta contact with the specified string used - * as a name and identifier. - * @param bonjourId ID of the contact - * @param bonjourService BonjourService responsible for handling chat with - * this contact - * @param name Display name of this contact - * @param ipAddress IP address of this contact - * @param port Port declared by this contact for direct point-to-point chat - * @param parentProvider the provider that created us. - */ - public ContactZeroconfImpl( - String bonjourId, - ProtocolProviderServiceZeroconfImpl parentProvider, - BonjourService bonjourService, - String name, - InetAddress ipAddress, - int port) - { - this.contactID = bonjourId; - this.parentProvider = parentProvider; - this.bonjourService = bonjourService; - this.name = name; - this.ipAddress = ipAddress; - this.port = port; - bonjourService.addContact(this); - } - - /** - * This method is only called when the contact is added to a new - * <tt>ContactGroupZeroconfImpl</tt> by the - * <tt>ContactGroupZeroconfImpl</tt> itself. - * - * @param newParentGroup the <tt>ContactGroupZeroconfImpl</tt> that is now - * parent of this <tt>ContactZeroconfImpl</tt> - */ - void setParentGroup(ContactGroupZeroconfImpl newParentGroup) - { - this.parentGroup = newParentGroup; - } - - /** - * Return the BonjourService - * @return BonjourService - */ - public BonjourService getBonjourService() - { - return bonjourService; - } - - /** - * Return the ClientThread responsible for handling with this contact - * @return ClientThread corresponding to the chat with this contact or null - * if no chat was started - */ - protected ClientThread getClientThread() - { - return thread; - } - - /** - * Set the ClientThread responsible for handling with this contact - * @param thread ClientThread corresponding to the chat with this contact - * or null if the chat is over - */ - protected void setClientThread(ClientThread thread) - { - this.thread = thread; - } - - /** - * Return the type of client - * @return Type of client used by this contact - */ - public int getClientType() - { - return clientType; - } - - /** - * Sets the type of client - * @param clientType Type of client used by this contact - */ - public void setClientType(int clientType) - { - this.clientType = clientType; - } - - /** - * 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 name; - } - - /** - * Returns the IP address declared by this Contact - * @return IP address declared by this Contact - */ - public InetAddress getIpAddress() - { - return ipAddress; - } - - /** - * Returns the TCP port declared by this Contact for direct chat - * @return the TCP port declared by this Contact for direct chat - */ - public int getPort() - { - return port; - } - - - /** - * Returns the status/private message displayed by this contact - * @return the status/private message displayed by this contact - */ - public String getMessage() - { - return message; - } - - /** - * Sets the status/private message displayed by this contact - * @param message the status/private message displayed by this contact - */ - public void setMessage(String message) - { - this.message = message; - } - - - /** - * 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 ZeroconfStatusEnum. - */ - public PresenceStatus getPresenceStatus() - { - return this.presenceStatus; - } - - /** - * Sets <tt>zeroconfPresenceStatus</tt> as the PresenceStatus that this - * contact is currently in. - * @param zeroconfPresenceStatus the <tt>ZeroconfPresenceStatus</tt> - * currently valid for this contact. - */ - public void setPresenceStatus(PresenceStatus zeroconfPresenceStatus) - { - this.presenceStatus = zeroconfPresenceStatus; - - if (zeroconfPresenceStatus == ZeroconfStatusEnum.OFFLINE) { - try - { - bonjourService.opSetPersPresence.unsubscribe(this); - } - catch (Exception ex) - { - logger.error(ex); - } - } - } - - /** - * Returns a reference to the protocol provider that created the contact. - * - * @return a refererence to an instance of the ProtocolProviderService - */ - public ProtocolProviderService getProtocolProvider() - { - return parentProvider; - } - - /** - * Determines whether or not this contact represents our own identity. - * - * @return true in case this is a contact that represents ourselves and - * false otherwise. - */ - public boolean isLocal() - { - return false; - } - - /** - * Returns the group that contains this contact. - * @return a reference to the <tt>ContactGroupZeroconfImpl</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. - */ - @Override - public String toString() - { - StringBuffer buff - = new StringBuffer("ContactZeroconfImpl[ 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; - } - - /** - * 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; - } - - /** - * Returns the persistent presence operation set that this contact belongs - * to. - * - * @return the <tt>OperationSetPersistentPresenceZeroconfImpl</tt> that - * this contact belongs to. - */ - public OperationSetPersistentPresenceZeroconfImpl - getParentPresenceOperationSet() - { - return (OperationSetPersistentPresenceZeroconfImpl)parentProvider - .getOperationSet(OperationSetPersistentPresence.class); - } - - /** - * 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; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/MessageZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/MessageZeroconfImpl.java deleted file mode 100644 index 3621edc..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/MessageZeroconfImpl.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import net.java.sip.communicator.service.protocol.*; - -/** - * Very simple message implementation for the Zeroconf protocol. - * - * @author Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - * @author Lubomir Marinov - */ -public class MessageZeroconfImpl - extends AbstractMessage -{ - - /** - * Message Type. - */ - private int type; - - /** - * Message type indicating that a stream is being created - */ - public static final int STREAM_OPEN = 0x1; - - /** - * Normal chat message - */ - public static final int MESSAGE = 0x2; - - /** - * Typing notification - */ - public static final int TYPING = 0x3; - - /** - * Message indicating that the stream is being closed - */ - public static final int STREAM_CLOSE = 0x4; - - /** - * Message indicating that the previsous message was delivered successfully - */ - public static final int DELIVERED = 0x5; - - /** - * Undefined message - */ - public static final int UNDEF = 0x6; - - /* - * The Baloon Icon color. (we probably won't ever use it) - */ - private int baloonColor = 0x7BB5EE; - - /* - * The Text Color. - */ - private int textColor = 0x000000; - - /* - * The font of the message. - */ - private String textFont = "Helvetica"; - - /* - * The size of the caracters composing the message. - */ - private int textSize = 12; - - /* - * The source contact id announced in the message. TODO: Could be set & - * checked to identify more precisely the contact in case several users - * would be sharing the same IP. - */ - private String contactID; - - /** - * Creates a message instance according to the specified parameters. - * - * @param content the message body - * @param contentEncoding message encoding or null for UTF8 - * @param contentType of the message - * @param type Type of message - */ - public MessageZeroconfImpl(String content, String contentEncoding, - String contentType, int type) - { - super(content, contentType, contentEncoding, null); - - this.type = type; - } - - /** - * Creates a message instance according to the specified parameters. - * - * @param type Type of message - * @param content the message body - * @param contentEncoding message encoding or null for UTF8 - */ - public MessageZeroconfImpl(String content, String contentEncoding, int type) - { - this(content, contentEncoding, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE, type); - } - - /** - * Returns the type of message. Always text/plain for Zeroconf, so null. - * - * @return null - */ - public int getType() - { - return type; - } - - /** - * Gets the baloon color declared in messages sent by iChat-like clients - * - * @return baloon color - */ - public int getBaloonColor() - { - return baloonColor; - } - - /** - * Sets the baloon color declared in messages sent by iChat-like clients - * - * @param baloonColor baloon color - */ - public void setBaloonColor(int baloonColor) - { - this.baloonColor = baloonColor; - } - - /** - * Returns the text color - * - * @return Text color - */ - public int getTextColor() - { - return textColor; - } - - /** - * Sets the text color - * - * @param textColor Text color - */ - public void setTextColor(int textColor) - { - this.textColor = textColor; - } - - /** - * Returns the text font - * - * @return Text font - */ - public String getTextFont() - { - return textFont; - } - - /** - * Sets the text color - * - * @param textFont Text font - */ - public void setTextFont(String textFont) - { - this.textFont = textFont; - } - - /** - * Returns the text size - * - * @return Text size - */ - public int getTextSize() - { - return textSize; - } - - /** - * Sets the text size - * - * @param textSize Text size - */ - public void setTextSize(int textSize) - { - this.textSize = textSize; - } - - /** - * Returns the contact's ID - * - * @return String representing the contact's ID - */ - public String getContactID() - { - return contactID; - } - - /** - * Sets the contact's ID - * - * @param contactID String representing the contact's ID - */ - public void setContactID(String contactID) - { - this.contactID = contactID; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java deleted file mode 100644 index d49118b..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.io.*; -import java.net.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * Instant messaging functionalities for the Zeroconf protocol. - * - * @author Christian Vincenot - * - */ -public class OperationSetBasicInstantMessagingZeroconfImpl - extends AbstractOperationSetBasicInstantMessaging -{ - private static final Logger logger - = Logger.getLogger(OperationSetBasicInstantMessagingZeroconfImpl.class); - - /** - * The currently valid persistent presence operation set.. - */ - private final OperationSetPersistentPresenceZeroconfImpl opSetPersPresence; - - /** - * The protocol provider that created us. - */ - private final ProtocolProviderServiceZeroconfImpl parentProvider; - - /** - * 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>OperationSetPersistentPresenceZeroconfImpl</tt> instance. - */ - public OperationSetBasicInstantMessagingZeroconfImpl( - ProtocolProviderServiceZeroconfImpl provider, - OperationSetPersistentPresenceZeroconfImpl opSetPersPresence) - { - this.opSetPersPresence = opSetPersPresence; - this.parentProvider = provider; - } - - @Override - public Message createMessage(String content, String contentType, - String encoding, String subject) - { - return new MessageZeroconfImpl(content, encoding, contentType, - MessageZeroconfImpl.MESSAGE); - } - - /** - * 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 Zeroconf 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 ContactZeroconfImpl) ) - throw new IllegalArgumentException( - "The specified contact is not a Zeroconf contact." - + to); - - MessageZeroconfImpl msg = - (MessageZeroconfImpl)createMessage(message.getContent()); - - deliverMessage(msg, (ContactZeroconfImpl)to); - } - - /** - * In case the to the <tt>to</tt> Contact corresponds to another zeroconf - * protocol provider registered with SIP Communicator, we deliver - * the message to them, in case the <tt>to</tt> Contact represents us, we - * fire a <tt>MessageReceivedEvent</tt>, and if <tt>to</tt> is simply - * a contact in our contact list, then we simply echo the message. - * - * @param message the <tt>Message</tt> the message to deliver. - * @param to the <tt>Contact</tt> that we should deliver the message to. - */ - private void deliverMessage(Message message, ContactZeroconfImpl to) - { - ClientThread thread = to.getClientThread(); - try - { - if (thread == null) - { - Socket sock; - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Creating a chat connexion to " - +to.getIpAddress()+":"+to.getPort()); - sock = new Socket(to.getIpAddress(), to.getPort()); - thread = new ClientThread(sock, to.getBonjourService()); - thread.setStreamOpen(); - thread.setContact(to); - to.setClientThread(thread); - thread.sendHello(); - if (to.getClientType() == ContactZeroconfImpl.GAIM) - { - try - { - Thread.sleep(300); - } - catch (InterruptedException ex) - { - logger.error(ex); - } - } - } - - //System.out.println("ZEROCONF: Message content => "+ - //message.getContent()); - thread.sendMessage((MessageZeroconfImpl) message); - - fireMessageDelivered(message, to); - } - catch (IOException ex) - { - logger.error(ex); - } - } - - /** - * 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. - */ - @Override - public void fireMessageReceived(Message message, Contact from) - { - super.fireMessageReceived(message, from); - } - - /** - * Determines whether the protocol provider (or the protocol itself) support - * sending and receiving offline messages. Most often this method would - * return true for protocols that support offline messages and false for - * those that don't. It is however possible for a protocol to support these - * messages and yet have a particular account that does not (i.e. feature - * not enabled on the protocol server). In cases like this it is possible - * for this method to return true even when offline messaging is not - * supported, and then have the sendMessage method throw an - * OperationFailedException with code - OFFLINE_MESSAGES_NOT_SUPPORTED. - * - * @return <tt>true</tt> if the protocol supports offline messages and - * <tt>false</tt> otherwise. - */ - public boolean isOfflineMessagingSupported() - { - return true; - } - - /** - * 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) - { - return contentType.equals(DEFAULT_MIME_TYPE); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetPersistentPresenceZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetPersistentPresenceZeroconfImpl.java deleted file mode 100644 index 412512e..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetPersistentPresenceZeroconfImpl.java +++ /dev/null @@ -1,852 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.net.*; -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 Zeroconf 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 Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - */ -public class OperationSetPersistentPresenceZeroconfImpl - extends AbstractOperationSetPersistentPresence<ProtocolProviderServiceZeroconfImpl> -{ - private static final Logger logger = - Logger.getLogger(OperationSetPersistentPresenceZeroconfImpl.class); - - /** - * The root of the zeroconf contact list. - */ - private ContactGroupZeroconfImpl contactListRoot = null; - - /** - * The currently active status message. - */ - private String statusMessage = "The truth is out there..."; - - /** - * Our default presence status. - */ - private PresenceStatus presenceStatus = ZeroconfStatusEnum.OFFLINE; - - /** - * 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 ProtocolProviderServiceZeroconfImpl instance that - * created us. - */ - public OperationSetPersistentPresenceZeroconfImpl( - ProtocolProviderServiceZeroconfImpl provider) - { - super(provider); - - contactListRoot = new ContactGroupZeroconfImpl("RootGroup", provider); - - //add our unregistration listener - parentProvider.addRegistrationStateChangeListener( - new UnregistrationListener()); - } - - /** - * 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) - { - ContactGroupZeroconfImpl newGroup - = new ContactGroupZeroconfImpl(groupName, parentProvider); - - ((ContactGroupZeroconfImpl)parent).addSubgroup(newGroup); - - this.fireServerStoredGroupEvent( - newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT); - } - - /** - * A Zeroconf Provider method to use for fast filling of a contact list. - * - * @param contactGroup the group to add - */ - public void addZeroconfGroup(ContactGroupZeroconfImpl contactGroup) - { - contactListRoot.addSubgroup(contactGroup); - } - - /** - * A Zeroconf 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 addZeroconfGroupAndFireEvent( - ContactGroupZeroconfImpl parent - , ContactGroupZeroconfImpl 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 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<PresenceStatus> getSupportedStatusSet() - { - return ZeroconfStatusEnum.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) - { - ContactZeroconfImpl zeroconfContact - = (ContactZeroconfImpl)contactToMove; - - ContactGroupZeroconfImpl parentZeroconfGroup - = findContactParent(zeroconfContact); - - parentZeroconfGroup.removeContact(zeroconfContact); - - //if this is a volatile contact then we haven't really subscribed to - //them so we'd need to do so here - if(!zeroconfContact.isPersistent()) - { - //first tell everyone that the volatile contact was removed - fireSubscriptionEvent(zeroconfContact - , parentZeroconfGroup - , SubscriptionEvent.SUBSCRIPTION_REMOVED); - - try - { - //now subscribe - this.subscribe(newParent, contactToMove.getAddress()); - - //now tell everyone that we've added the contact - fireSubscriptionEvent(zeroconfContact - , newParent - , SubscriptionEvent.SUBSCRIPTION_CREATED); - } - catch (Exception ex) - { - logger.error("Failed to move contact " - + zeroconfContact.getAddress() - , ex); - } - } - else - { - ( (ContactGroupZeroconfImpl) newParent) - .addContact(zeroconfContact); - - fireSubscriptionMovedEvent(contactToMove - , parentZeroconfGroup - , 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; - - //ICI: changer le statut du plugin Zeroconf!! - parentProvider.getBonjourService().changeStatus(status); - - this.fireProviderStatusChangeEvent(oldPresenceStatus); - - } - - /** - * 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>ContactZeroconfImpl</tt> whose status we'd like - * to set. - * @param newStatus the new status we'd like to set to <tt>contact</tt>. - */ - public void changePresenceStatusForContact(ContactZeroconfImpl 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. - */ - protected void changePresenceStatusForAllContacts(ContactGroup parent, - PresenceStatus newStatus) - { - //first set the status for contacts in this group - Iterator<Contact> childContacts = parent.contacts(); - - while(childContacts.hasNext()) - { - ContactZeroconfImpl contact - = (ContactZeroconfImpl)childContacts.next(); - - if(findProviderForZeroconfUserID(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<ContactGroup> subgroups = parent.subgroups(); - - while(subgroups.hasNext()) - { - ContactGroup subgroup = subgroups.next(); - changePresenceStatusForAllContacts(subgroup, newStatus); - } - } - - /** - * Returns the group that is parent of the specified zeroconfGroup or null - * if no parent was found. - * @param zeroconfGroup the group whose parent we're looking for. - * @return the ContactGroupZeroconfImpl instance that zeroconfGroup - * belongs to or null if no parent was found. - */ - public ContactGroupZeroconfImpl findGroupParent( - ContactGroupZeroconfImpl zeroconfGroup) - { - return contactListRoot.findGroupParent(zeroconfGroup); - } - - /** - * Returns the group that is parent of the specified zeroconfContact or - * null if no parent was found. - * @param zeroconfContact the contact whose parent we're looking for. - * @return the ContactGroupZeroconfImpl instance that zeroconfContact - * belongs to or null if no parent was found. - */ - public ContactGroupZeroconfImpl findContactParent( - ContactZeroconfImpl zeroconfContact) - { - return (ContactGroupZeroconfImpl)zeroconfContact - .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 - { - ContactGroupZeroconfImpl zeroconfGroup - = (ContactGroupZeroconfImpl)group; - - ContactGroupZeroconfImpl parent = findGroupParent(zeroconfGroup); - - if(parent == null){ - throw new IllegalArgumentException( - "group " + group - + " does not seem to belong to this protocol's contact list."); - } - - parent.removeSubGroup(zeroconfGroup); - - this.fireServerStoredGroupEvent( - zeroconfGroup, ServerStoredGroupEvent.GROUP_REMOVED_EVENT); - } - - /** - * 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) - { - ((ContactGroupZeroconfImpl)group).setGroupName(newName); - - this.fireServerStoredGroupEvent( - 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 - { - /* ContactZeroconfImpl contact = new ContactZeroconfImpl( - contactIdentifier, - parentProvider, - null, null, null, 0); - - ((ContactGroupZeroconfImpl)parent).addContact(contact); - - fireSubscriptionEvent(contact, - parent, - SubscriptionEvent.SUBSCRIPTION_CREATED); - //if the newly added contact corresponds to another provider - set their - //status accordingly - ProtocolProviderServiceZeroconfImpl gibProvider - = findProviderForZeroconfUserID(contactIdentifier); - if(gibProvider != null) - { - OperationSetPersistentPresence opSetPresence - = (OperationSetPersistentPresence)gibProvider.getOperationSet( - OperationSetPersistentPresence.class); - - changePresenceStatusForContact( - contact - , (ZeroconfStatusEnum)opSetPresence.getPresenceStatus()); - } - else - { - //otherwise - since we are not a real protocol, we set the contact - //presence status ourselves - changePresenceStatusForContact(contact, getPresenceStatus()); - } - - //notify presence listeners for the status change. - fireContactPresenceStatusChangeEvent(contact - , parent - , ZeroconfStatusEnum.OFFLINE); - */} - - - - /** - * 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 - { - String name = contact.getAddress(); - - ContactGroupZeroconfImpl parentGroup - = (ContactGroupZeroconfImpl)((ContactZeroconfImpl)contact) - .getParentContactGroup(); - - //parentGroup.removeContact((ContactZeroconfImpl)contact); - - BonjourService service = - ((ProtocolProviderServiceZeroconfImpl)contact.getProtocolProvider()) - .getBonjourService(); - //TODO: better check with IP - service.removeContact(name,null); - - fireSubscriptionEvent(contact, - ((ContactZeroconfImpl)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) - { - return null; - } - - /** - * Looks for a zeroconf protocol provider registered for a user id matching - * <tt>zeroconfUserID</tt>. - * - * @param zeroconfUserID the ID of the Zeroconf user whose corresponding - * protocol provider we'd like to find. - * @return ProtocolProviderServiceZeroconfImpl a zeroconf protocol - * provider registered for a user with id <tt>zeroconfUserID</tt> or null - * if there is no such protocol provider. - */ - public ProtocolProviderServiceZeroconfImpl - findProviderForZeroconfUserID(String zeroconfUserID) - { - BundleContext bc = ZeroconfActivator.getBundleContext(); - - String osgiQuery = "(&" + - "(" + ProtocolProviderFactory.PROTOCOL + - "=" + ProtocolNames.ZEROCONF + ")" + - "(" + ProtocolProviderFactory.USER_ID + - "=" + zeroconfUserID + "))"; - - 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 (ProtocolProviderServiceZeroconfImpl)bc.getService(refs[0]); - } - - return null; - } - - /** - * 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) - { - ContactGroupZeroconfImpl newGroup - = new ContactGroupZeroconfImpl( - ContactGroupZeroconfImpl.createNameFromUID(groupUID) - , parentProvider); - newGroup.setResolved(false); - - //if parent is null then we're adding under root. - if(parentGroup == null) - parentGroup = getServerStoredContactListRoot(); - - ((ContactGroupZeroconfImpl)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 zeroconf 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 (logger.isDebugEnabled()) - logger.debug("ZEROCONF : The Zeroconf provider changed state from: " - + evt.getOldState() - + " to: " + evt.getNewState()); - - //send event notifications saying that all our buddies are - //offline. The Zeroconf protocol does not implement top level buddies - //nor subgroups for top level groups so a simple nested loop - //would be enough. - Iterator<ContactGroup> groupsIter - = getServerStoredContactListRoot().subgroups(); - while (groupsIter.hasNext()) - { - ContactGroup group = groupsIter.next(); - Iterator<Contact> contactsIter = group.contacts(); - - while (contactsIter.hasNext()) - { - ContactZeroconfImpl contact - = (ContactZeroconfImpl) contactsIter.next(); - - PresenceStatus oldContactStatus - = contact.getPresenceStatus(); - - /* We set contacts to OFFLINE and send an event so that external listeners - * can be aware that the contacts are reachable anymore. Dunno if that's - * a good idea. Can be erased if not. Contacts clean is directly done by the - * contact status change handler. - */ - if (!oldContactStatus.isOnline()) - { - //contact.setPresenceStatus(ZeroconfStatusEnum.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. - */ - public ContactGroupZeroconfImpl getNonPersistentGroup() - { - for (int i = 0; - i < getServerStoredContactListRoot().countSubgroups(); - i++) - { - ContactGroupZeroconfImpl gr = - (ContactGroupZeroconfImpl)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. - * @return the newly created volatile contact. - * @param bonjourService BonjourService responsible for the chat with this contact - * @param name Display name of the contact - * @param ip IP address of the contact - * @param port Port declared by the contact for direct chat - * @param contactAddress the address of the volatile contact we'd like to - * create. - */ - public ContactZeroconfImpl createVolatileContact(String contactAddress, - BonjourService bonjourService, - String name, - InetAddress ip, - int port) - { - //First create the new volatile contact; - ContactZeroconfImpl newVolatileContact - = new ContactZeroconfImpl(contactAddress, - this.parentProvider, bonjourService, name, ip, port); - newVolatileContact.setPersistent(false); - - - //Check whether a volatile group already exists and if not create - //one - ContactGroupZeroconfImpl theVolatileGroup = getNonPersistentGroup(); - - - //if the parent volatile group is null then we create it - if (theVolatileGroup == null) - { - theVolatileGroup = new ContactGroupZeroconfImpl( - "Bonjour" - , 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; - } - - public Contact getLocalContact() - { - return null; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetTypingNotificationsZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetTypingNotificationsZeroconfImpl.java deleted file mode 100644 index db397b8..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetTypingNotificationsZeroconfImpl.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import net.java.sip.communicator.service.protocol.*; - -/** - * Implements typing notifications for the Zeroconf protocol. The operation - * set would simply mirror all outgoing typing notifications and make them - * appear as incoming events generated by the contact that we are currently - * writing a message to. - * - * @author Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - */ -public class OperationSetTypingNotificationsZeroconfImpl - extends AbstractOperationSetTypingNotifications<ProtocolProviderServiceZeroconfImpl> -{ - - /** - * Creates a new instance of this operation set and keeps the parent - * provider as a reference. - * - * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt> - * that created us and that we'll use for retrieving the underlying aim - * connection. - */ - OperationSetTypingNotificationsZeroconfImpl( - ProtocolProviderServiceZeroconfImpl provider) - { - super(provider); - } - - /** - * Sends a notification to <tt>notifiedContatct</tt> that we have entered - * <tt>typingState</tt>. - * - * @param notifiedContact the <tt>Contact</tt> to notify - * @param typingState the typing state that we have entered. - * - * @throws java.lang.IllegalStateException if the underlying stack is - * not registered and initialized. - * @throws java.lang.IllegalArgumentException if <tt>notifiedContact</tt> is - * not an instance belonging to the underlying implementation. - */ - public void sendTypingNotification(Contact notifiedContact, int typingState) - throws IllegalStateException, IllegalArgumentException - { - if( !(notifiedContact instanceof ContactZeroconfImpl) ) - throw new IllegalArgumentException( - "The specified contact is not a Zeroconf contact." - + notifiedContact); - - ContactZeroconfImpl to = (ContactZeroconfImpl)notifiedContact; - - ClientThread thread = to.getClientThread(); - if (thread == null) return;/*throw new IllegalStateException( - "No communication channel opened to chat with this contact");*/ - - if (typingState != STATE_TYPING) - return; - - MessageZeroconfImpl message = - new MessageZeroconfImpl("",null, MessageZeroconfImpl.TYPING); - thread.sendMessage(message); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolIconZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolIconZeroconfImpl.java deleted file mode 100644 index 4c6ce07..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolIconZeroconfImpl.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -import org.jitsi.service.resources.*; -import org.osgi.framework.*; - -/** - * Represents the Zeroconf protocol icon. Implements the <tt>ProtocolIcon</tt> - * interface in order to provide a Zeroconf logo image in two different sizes. - * - * @author Christian Vincenot - * @author Jonathan Martin - */ -public class ProtocolIconZeroconfImpl - implements ProtocolIcon -{ - private static Logger logger - = Logger.getLogger(ProtocolIconZeroconfImpl.class); - - private static ResourceManagementService resourcesService; - - /** - * 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, - getImageInBytes("service.protocol.zeroconf.ZEROCONF_16x16")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_32x32, - getImageInBytes("service.protocol.zeroconf.ZEROCONF_32x32")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_48x48, - getImageInBytes("service.protocol.zeroconf.ZEROCONF_48x48")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_64x64, - getImageInBytes("service.protocol.zeroconf.ZEROCONF_64x64")); - } - - /** - * A hash table containing the protocol icon in different sizes. - */ - private static Hashtable<String, String> iconPathsTable - = new Hashtable<String, String>(); - static - { - iconPathsTable.put(ProtocolIcon.ICON_SIZE_16x16, - getResources().getImagePath( - "service.protocol.zeroconf.ZEROCONF_16x16")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_32x32, - getResources().getImagePath( - "service.protocol.zeroconf.ZEROCONF_32x32")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_48x48, - getResources().getImagePath( - "service.protocol.zeroconf.ZEROCONF_48x48")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_64x64, - getResources().getImagePath( - "service.protocol.zeroconf.ZEROCONF_64x64")); - } - - /** - * Implements the <tt>ProtocolIcon.getSupportedSizes()</tt> method. Returns - * an iterator to a set containing the supported icon sizes. - * @return an iterator to a set containing the supported icon sizes - */ - public Iterator<String> getSupportedSizes() - { - return iconsTable.keySet().iterator(); - } - - /** - * Returns TRUE if a icon with the given size is supported, FALSE-otherwise. - * @param iconSize Icon size - * @return True if this 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 Icon image - */ - public byte[] getIcon(String iconSize) - { - return iconsTable.get(iconSize); - } - - /** - * Returns a path to the icon with the given size. - * @param iconSize the size of the icon we're looking for - * @return the path to the icon with the given size - */ - public String getIconPath(String iconSize) - { - return iconPathsTable.get(iconSize); - } - - /** - * Returns the icon image used to represent the protocol connecting state. - * @return the icon image used to represent the protocol connecting state - */ - public byte[] getConnectingIcon() - { - return getImageInBytes("zeroconfOnlineIcon"); - } - - /** - * Returns the byte representation of the image corresponding to the given - * identifier. - * - * @param imageID the identifier of the image - * @return the byte representation of the image corresponding to the given - * identifier. - */ - public static byte[] getImageInBytes(String imageID) - { - InputStream in = getResources(). - getImageInputStream(imageID); - - if (in == null) - return null; - byte[] image = null; - try - { - image = new byte[in.available()]; - - in.read(image); - } - catch (IOException e) - { - logger.error("Failed to load image:" + imageID, e); - } - - return image; - } - - /** - * Returns the <tt>ResourceManagementService</tt>. - * - * @return the <tt>ResourceManagementService</tt> - */ - public static ResourceManagementService getResources() - { - if (resourcesService == null) - { - ServiceReference serviceReference = ZeroconfActivator.bundleContext - .getServiceReference(ResourceManagementService.class.getName()); - - if(serviceReference == null) - return null; - - resourcesService - = (ResourceManagementService)ZeroconfActivator.bundleContext - .getService(serviceReference); - } - - return resourcesService; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderFactoryZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderFactoryZeroconfImpl.java deleted file mode 100644 index e68033f..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderFactoryZeroconfImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -import org.osgi.framework.*; - -/** - * The Zeroconf protocol provider factory creates instances of the Zeroconf - * protocol provider service. One Service instance corresponds to one account. - * - * - * @author Christian Vincenot - * @author Maxime Catelin - */ -public class ProtocolProviderFactoryZeroconfImpl - extends ProtocolProviderFactory -{ - - /** - * Creates an instance of the ProtocolProviderFactoryZeroconfImpl. - */ - public ProtocolProviderFactoryZeroconfImpl() - { - super(ZeroconfActivator.getBundleContext(), ProtocolNames.ZEROCONF); - } - - /** - * 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. - */ - @Override - public AccountID installAccount( String userIDStr, - Map<String, String> accountProperties) - { - BundleContext context - = ZeroconfActivator.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 ZeroconfAccountID(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 access - //the configuration service and check for a stored password. - this.storeAccount(accountID, false); - - accountID = loadAccount(accountProperties); - - return accountID; - } - - @Override - protected AccountID createAccountID(String userID, Map<String, String> accountProperties) - { - return new ZeroconfAccountID(userID, accountProperties); - } - - @Override - protected ProtocolProviderService createService(String userID, - AccountID accountID) - { - ProtocolProviderServiceZeroconfImpl service = - new ProtocolProviderServiceZeroconfImpl(); - - service.initialize(userID, accountID); - return service; - } - - @Override - public void modifyAccount( ProtocolProviderService protocolProvider, - Map<String, String> accountProperties) - throws NullPointerException - { - // TODO Auto-generated method stub - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderServiceZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderServiceZeroconfImpl.java deleted file mode 100644 index 7fe916f..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderServiceZeroconfImpl.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; - -/** - * An implementation of the protocol provider service over the Zeroconf protocol - * - * @author Christian Vincenot - * @author Maxime Catelin - */ -public class ProtocolProviderServiceZeroconfImpl - extends AbstractProtocolProviderService -{ - /** - * The logger for this class. - */ - private static final Logger logger = - Logger.getLogger(ProtocolProviderServiceZeroconfImpl.class); - - /** - * We use this to lock access to initialization. - */ - private final Object initializationLock = new Object(); - - /** - * The id of the account that this protocol provider represents. - */ - private AccountID accountID = null; - - /** - * Indicates whether or not the provider is initialized and ready for use. - */ - private boolean isInitialized = false; - - /** - * The logo corresponding to the zeroconf protocol. - */ - private final ProtocolIconZeroconfImpl zeroconfIcon - = new ProtocolIconZeroconfImpl(); - - /** - * 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 BonjourService corresponding to this ProtocolProviderService - */ - - private BonjourService bonjourService; - - /** - * The default constructor for the Zeroconf protocol provider. - */ - public ProtocolProviderServiceZeroconfImpl() - { - if (logger.isTraceEnabled()) - logger.trace("Creating a zeroconf provider."); - } - - /** - * 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 Bonjour Service that handles the Bonjour protocol stack. - * - *@return the Bonjour Service linked with this Protocol Provider - */ - public BonjourService getBonjourService() - { - return bonjourService; - } - - /** - * 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 zeroconf 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 - OperationSetPersistentPresenceZeroconfImpl persistentPresence = - new OperationSetPersistentPresenceZeroconfImpl(this); - - addSupportedOperationSet( - OperationSetPersistentPresence.class, - persistentPresence); - //register it once again for those that simply need presence and - //won't be smart enough to check for a persistent presence - //alternative - addSupportedOperationSet( - OperationSetPresence.class, - persistentPresence); - - //initialize the IM operation set - addSupportedOperationSet( - OperationSetBasicInstantMessaging.class, - new OperationSetBasicInstantMessagingZeroconfImpl( - this, - persistentPresence)); - - //initialize the typing notifications operation set - addSupportedOperationSet( - OperationSetTypingNotifications.class, - new OperationSetTypingNotificationsZeroconfImpl(this)); - - isInitialized = true; - } - } - - /** - * 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 ProtocolNames.ZEROCONF; - } - - /** - * Returns the state of the registration of this protocol provider with - * the corresponding registration service. - * - * @return ProviderRegistrationState - */ - public RegistrationState getRegistrationState() - { - return currentRegistrationState; - } - - /** - * Starts the registration process. - * - * @param authority the security authority that will be used for - * resolving any security challenges that may be returned during the - * registration or at any moment while wer're registered. - * @throws OperationFailedException with the corresponding code it the - * registration fails for some reason (e.g. a networking error or an - * implementation problem). - */ - public void register(SecurityAuthority authority) - throws OperationFailedException - { - //we don't need a password here since there's no server in - //zeroconf. - - RegistrationState oldState = currentRegistrationState; - currentRegistrationState = RegistrationState.REGISTERED; - - - //ICI : creer le service Zeroconf !! - if (logger.isInfoEnabled()) - logger.info("ZEROCONF: Starting the service"); - this.bonjourService = new BonjourService(5298, this); - - //bonjourService.changeStatus(ZeroconfStatusEnum.ONLINE); - - 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; - } - if (logger.isTraceEnabled()) - logger.trace("Killing the Zeroconf Protocol Provider."); - - if(isRegistered()) - { - try - { - //do the unregistration - unregister(); - } - catch (OperationFailedException ex) - { - //we're shutting down so we need to silence the exception here - logger.error( - "Failed to properly unregister before shutting down. " - + getAccountID() - , ex); - } - } - - isInitialized = false; - } - - /** - * Ends the registration of this protocol provider with the current - * registration service. - * - * @throws OperationFailedException with the corresponding code it the - * registration fails for some reason (e.g. a networking error or an - * implementation problem). - */ - public void unregister() - throws OperationFailedException - { - RegistrationState oldState = currentRegistrationState; - currentRegistrationState = RegistrationState.UNREGISTERED; - - if(bonjourService != null) - bonjourService.shutdown(); - - fireRegistrationStateChanged( - oldState - , currentRegistrationState - , RegistrationStateChangeEvent.REASON_USER_REQUEST - , null); - } - - /* - * (non-Javadoc) - * - * @see net.java.sip.communicator.service.protocol.ProtocolProviderService# - * isSignallingTransportSecure() - */ - public boolean isSignalingTransportSecure() - { - return false; - } - - /** - * Returns the "transport" protocol of this instance used to carry the - * control channel for the current protocol service. - * - * @return The "transport" protocol of this instance: TCP. - */ - public TransportProtocol getTransportProtocol() - { - return TransportProtocol.TCP; - } - - /** - * Returns the zeroconf protocol icon. - * @return the zeroconf protocol icon - */ - public ProtocolIcon getProtocolIcon() - { - return zeroconfIcon; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfAccountID.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfAccountID.java deleted file mode 100644 index 64e22c7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfAccountID.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * The Zeroconf implementation of a sip-communicator AccountID - * - * @author Christian Vincenot - */ -public class ZeroconfAccountID - extends AccountID -{ - /* Firstname, lastname, mail address */ - private String first = null; - private String last = null; - private String mail = null; - - private boolean rememberContacts = false; - - /** - * Creates a zeroconf account id from the specified id and account - * properties. - * @param userID id identifying this account - * @param accountProperties any other properties necessary for the account. - */ - ZeroconfAccountID(String userID, Map<String, String> accountProperties) - { - super(userID, - accountProperties, - ProtocolNames.ZEROCONF, - "zeroconf.org"); - - first = accountProperties.get("first"); - last = accountProperties.get("last"); - mail = accountProperties.get("mail"); - - rememberContacts = - new Boolean(accountProperties.get("rememberContacts")) - .booleanValue(); - } - - /** - * Returns a String representing the firstname of this user. - * @return String representing the firstname of this user. - */ - public String getFirst() - { - return first; - } - - /** - * Returns a String representing the lastname of this user. - * @return String representing the lastname of this user. - */ - public String getLast() - { - return last; - } - - /** - * Returns a String representing the mail address of this user. - * @return String representing the mail address of this user. - */ - public String getMail() - { - return mail; - } - - /** - * Returns a boolean indicating if we store the contacts we meet or not. - * @return boolean indicating if we store the contacts we meet or not. - */ - public boolean isRememberContacts() - { - return rememberContacts; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfActivator.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfActivator.java deleted file mode 100644 index 2544bab..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfActivator.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -import org.osgi.framework.*; - -/** - * Loads the Zeroconf provider factory and registers its services in the OSGI - * bundle context. - * - * @author Christian Vincenot - * @author Maxime Catelin - */ -public class ZeroconfActivator - implements BundleActivator -{ - private static final Logger logger - = Logger.getLogger(ZeroconfActivator.class); - - /** - * A reference to the registration of our Zeroconf protocol provider - * factory. - */ - private ServiceRegistration zeroconfPpFactoryServReg = null; - - /** - * A reference to the Zeroconf protocol provider factory. - */ - private static ProtocolProviderFactoryZeroconfImpl - zeroconfProviderFactory = null; - - /** - * The currently valid bundle context. - */ - static BundleContext bundleContext = null; - - - /** - * Called when this bundle is started. In here we'll export the - * zeroconf 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 - { -// logger.setLevelAll(); - - bundleContext = context; - - Hashtable<String, String> hashtable = new Hashtable<String, String>(); - hashtable.put(ProtocolProviderFactory.PROTOCOL, "Zeroconf"); - - zeroconfProviderFactory = new ProtocolProviderFactoryZeroconfImpl(); - - //register the zeroconf provider factory. - zeroconfPpFactoryServReg = context.registerService( - ProtocolProviderFactory.class.getName(), - zeroconfProviderFactory, - hashtable); - - if (logger.isInfoEnabled()) - logger.info("Zeroconf protocol implementation [STARTED]."); - } - - /** - * Returns a reference to the bundle context that we were started with. - * @return a reference to the BundleContext instance that we were started - * witn. - */ - public static BundleContext getBundleContext() - { - return bundleContext; - } - - /** - * Retrurns a reference to the protocol provider factory that we have - * registered. - * @return a reference to the <tt>ProtocolProviderFactoryJabberImpl</tt> - * instance that we have registered from this package. - */ - public static ProtocolProviderFactoryZeroconfImpl getProtocolProviderFactory() - { - return zeroconfProviderFactory; - } - - - /** - * 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 - { - zeroconfProviderFactory.stop(); - zeroconfPpFactoryServReg.unregister(); - - if (logger.isInfoEnabled()) - logger.info("Zeroconf protocol implementation [STOPPED]."); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfStatusEnum.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfStatusEnum.java deleted file mode 100644 index 3fe2745..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfStatusEnum.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * An implementation of <tt>PresenceStatus</tt> that enumerates all states that - * a Zeroconf contact can fall into. - * - * @author Christian Vincenot - * @author Jonathan Martin - */ -public class ZeroconfStatusEnum - extends PresenceStatus -{ - - /** - * Indicates an Offline status or status with 0 connectivity. - */ - public static final ZeroconfStatusEnum OFFLINE - = new ZeroconfStatusEnum( - 0, - "Offline", - ProtocolIconZeroconfImpl.getImageInBytes( - "service.protocol.zeroconf.OFFLINE_STATUS_ICON")); - - /** - * The DND status. Indicates that the user has connectivity but prefers - * not to be contacted. - */ - public static final ZeroconfStatusEnum DO_NOT_DISTURB - = new ZeroconfStatusEnum( - 30, - "Do Not Disturb",//, "Do Not Disturb", - ProtocolIconZeroconfImpl.getImageInBytes( - "service.protocol.zeroconf.DND_STATUS_ICON")); - - /** - * The Invisible status. Indicates that the user has connectivity even - * though it may appear otherwise to others, to whom she would appear to be - * offline. - */ - public static final ZeroconfStatusEnum INVISIBLE - = new ZeroconfStatusEnum( - 45, - "Invisible", - ProtocolIconZeroconfImpl.getImageInBytes( - "service.protocol.zeroconf.INVISIBLE_STATUS_ICON")); - - /** - * The Online status. Indicate that the user is able and willing to - * communicate. - */ - public static final ZeroconfStatusEnum ONLINE - = new ZeroconfStatusEnum( - 65, - "Available",//, "Online" - ProtocolIconZeroconfImpl.getImageInBytes( - "service.protocol.zeroconf.ONLINE_STATUS_ICON")); - - - /** - * Initialize the list of supported status states. - */ - private static List<PresenceStatus> supportedStatusSet = new LinkedList<PresenceStatus>(); - static - { - supportedStatusSet.add(OFFLINE); - supportedStatusSet.add(DO_NOT_DISTURB); - - /* INVISIBLE STATUS could be supported by unregistering JmDNS and - * accepting unknown contacts' messages */ - //supportedStatusSet.add(INVISIBLE); - - supportedStatusSet.add(ONLINE); - } - - /** - * Creates an instance of <tt>ZeroconfPresneceStatus</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 ZeroconfStatusEnum(int status, - String statusName, - byte[] statusIcon) - { - super(status, statusName, statusIcon); - } - - /** - * Returns an iterator over all status instances supproted by the zeroconf - * provider. - * @return an <tt>Iterator</tt> over all status instances supported by the - * zeroconf provider. - */ - static Iterator<PresenceStatus> supportedStatusSet() - { - return supportedStatusSet.iterator(); - } - - /** - * @param status String representation of the status - * @return ZeroconfStatusEnum corresponding the supplied String value - */ - static ZeroconfStatusEnum statusOf(String status) - { - Iterator<PresenceStatus> statusIter = supportedStatusSet(); - while (statusIter.hasNext()) - { - ZeroconfStatusEnum state = (ZeroconfStatusEnum)statusIter.next(); - if (state.statusName.equalsIgnoreCase(status)) - return state; - } - return null; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java deleted file mode 100644 index 0baaff4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; - -import net.java.sip.communicator.util.*; - -/** - * A table of DNS entries. This is a hash table which - * can handle multiple entries with the same name. - * <p/> - * Storing multiple entries with the same name is implemented using a - * linked list of <code>CacheNode</code>'s. - * <p/> - * The current implementation of the API of DNSCache does expose the - * cache nodes to clients. Clients must explicitly deal with the nodes - * when iterating over entries in the cache. Here's how to iterate over - * all entries in the cache: - * <pre> - * for (Iterator i=dnscache.iterator(); i.hasNext(); ) - * { - * for ( DNSCache.CacheNode n = (DNSCache.CacheNode) i.next(); - * n != null; - * n.next()) - * { - * DNSEntry entry = n.getValue(); - * ...do something with entry... - * } - * } - * </pre> - * <p/> - * And here's how to iterate over all entries having a given name: - * <pre> - * for ( DNSCache.CacheNode n = (DNSCache.CacheNode) dnscache.find(name); - * n != null; - * n.next()) - * { - * DNSEntry entry = n.getValue(); - * ...do something with entry... - * } - * </pre> - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer, Rick Blair - */ -class DNSCache -{ - private static Logger logger = Logger.getLogger(DNSCache.class.toString()); - // Implementation note: - // We might completely hide the existence of CacheNode's in a future version - // of DNSCache. But this will require to implement two (inner) classes for - // the iterators that will be returned by method <code>iterator()</code> and - // method <code>find(name)</code>. - // Since DNSCache is not a public class, it does not seem worth the effort - // to clean its API up that much. - - // [PJYF Oct 15 2004] This should implements Collections - // that would be amuch cleaner implementation - - /** - * The number of DNSEntry's in the cache. - */ - private int size; - - /** - * The hashtable used internally to store the entries of the cache. - * Keys are instances of String. The String contains an unqualified service - * name. - * Values are linked lists of CacheNode instances. - */ - private HashMap<String, CacheNode> hashtable; - - /** - * Cache nodes are used to implement storage of multiple DNSEntry's of the - * same name in the cache. - */ - public static class CacheNode - { - private DNSEntry value; - private CacheNode next; - - public CacheNode(DNSEntry value) - { - this.value = value; -// String SLevel = System.getProperty("jmdns.debug"); -// if (SLevel == null) -// SLevel = "INFO"; -// logger.setLevel(Level.parse(SLevel)); - } - - public CacheNode next() - { - return next; - } - - public DNSEntry getValue() - { - return value; - } - } - - - /** - * Create a table with a given initial size. - * @param size initial size. - */ - public DNSCache(final int size) - { - hashtable = new HashMap<String, CacheNode>(size); - -// String SLevel = System.getProperty("jmdns.debug"); -// if (SLevel == null) SLevel = "INFO"; -// logger.setLevel(Level.parse(SLevel)); - } - - /** - * Clears the cache. - */ - public synchronized void clear() - { - hashtable.clear(); - size = 0; - } - - /** - * Adds an entry to the table. - * @param entry added to the table. - */ - public synchronized void add(final DNSEntry entry) - { - //logger.log("DNSCache.add("+entry.getName()+")"); - CacheNode newValue = new CacheNode(entry); - CacheNode node = hashtable.get(entry.getName()); - if (node == null) - { - hashtable.put(entry.getName(), newValue); - } - else - { - newValue.next = node.next; - node.next = newValue; - } - size++; - } - - /** - * Remove a specific entry from the table. - * @param entry removed from table. - * @return Returns true if the entry was found. - */ - public synchronized boolean remove(DNSEntry entry) - { - CacheNode node = hashtable.get(entry.getName()); - if (node != null) - { - if (node.value == entry) - { - if (node.next == null) - { - hashtable.remove(entry.getName()); - } - else - { - hashtable.put(entry.getName(), node.next); - } - size--; - return true; - } - - CacheNode previous = node; - node = node.next; - while (node != null) - { - if (node.value == entry) - { - previous.next = node.next; - size--; - return true; - } - previous = node; - node = node.next; - } - ; - } - return false; - } - - /** - * Get a matching DNS entry from the table (using equals). - * @param entry to be found in table. - * @return Returns the entry that was found. - */ - public synchronized DNSEntry get(DNSEntry entry) - { - for (CacheNode node = find(entry.getName()); node != null; node = node.next) - { - if (node.value.equals(entry)) - { - return node.value; - } - } - return null; - } - - /** - * Get a matching DNS entry from the table. - * @param name - * @param type - * @param clazz - * @return Return the entry if found, null otherwise. - */ - public synchronized DNSEntry get(String name, int type, int clazz) - { - for (CacheNode node = find(name); node != null; node = node.next) - { - if (node.value.type == type && node.value.clazz == clazz) - { - return node.value; - } - } - return null; - } - - /** - * Iterates over all cache nodes. - * The iterator returns instances of DNSCache.CacheNode. - * Each instance returned is the first node of a linked list. - * To retrieve all entries, one must iterate over this linked list. See - * code snippets in the header of the class. - * @return Returns iterator with instances of DNSCache.CacheNode. - */ - public Iterator<DNSCache.CacheNode> iterator() - { - return Collections.unmodifiableCollection(hashtable.values()).iterator(); - } - - /** - * Iterate only over items with matching name. - * If an instance is returned, it is the first node of a linked list. - * To retrieve all entries, one must iterate over this linked list. - * @param name to be found. - * @return Returns an instance of DNSCache.CacheNode or null. - */ - public synchronized CacheNode find(String name) - { - return hashtable.get(name); - } - - /** - * List all entries for debugging. - */ - public synchronized void print() - { - for (Iterator<CacheNode> i = iterator(); i.hasNext();) - { - for (CacheNode n = i.next(); n != null; n = n.next) - { - if (logger.isInfoEnabled()) - logger.info(n.value.toString()); - } - } - } - - @Override - public synchronized String toString() - { - StringBuffer aLog = new StringBuffer(); - aLog.append("\t---- cache ----"); - for (Iterator<CacheNode> i = iterator(); i.hasNext();) - { - for (CacheNode n = i.next(); n != null; n = n.next) - { - aLog.append("\n\t\t" + n.value); - } - } - return aLog.toString(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java deleted file mode 100644 index 8792c8d..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff, Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -/** - * DNS constants. - * - * @version %I%, %G% - * @author Arthur van Hoff, Jeff Sonstein, - * Werner Randelshofer, Pierre Frisch, Rick Blair - */ -public final class DNSConstants -{ - - // changed to final class - jeffs - final static String MDNS_GROUP = "224.0.0.251"; - final static String MDNS_GROUP_IPV6 = "FF02::FB"; - final static int MDNS_PORT = 5353; - final static int DNS_PORT = 53; - // default one hour TTL - final static int DNS_TTL = 60 * 60; - // two hour TTL (draft-cheshire-dnsext-multicastdns.txt ch 13) - // final static int DNS_TTL = 120 * 60; - - final static int MAX_MSG_TYPICAL = 1460; - final static int MAX_MSG_ABSOLUTE = 8972; - - final static int FLAGS_QR_MASK = 0x8000; // Query response mask - final static int FLAGS_QR_QUERY = 0x0000; // Query - final static int FLAGS_QR_RESPONSE = 0x8000;// Response - - public final static int FLAGS_AA = 0x0400; // Authorative answer - final static int FLAGS_TC = 0x0200; // Truncated - final static int FLAGS_RD = 0x0100; // Recursion desired - public final static int FLAGS_RA = 0x8000; // Recursion available - - final static int FLAGS_Z = 0x0040; // Zero - final static int FLAGS_AD = 0x0020; // Authentic data - final static int FLAGS_CD = 0x0010; // Checking disabled - - // Final Static Internet - public final static int CLASS_IN = 1; - // CSNET - final static int CLASS_CS = 2; - // CHAOS - final static int CLASS_CH = 3; - // Hesiod - final static int CLASS_HS = 4; - // Used in DNS UPDATE [RFC 2136] - final static int CLASS_NONE = 254; - // Not a DNS class, but a DNS query class, meaning "all classes" - final static int CLASS_ANY = 255; - // Multicast DNS uses the bottom 15 bits to identify the record class... - final static int CLASS_MASK = 0x7FFF; - // ... and the top bit indicates that all other cached records are now invalid - public final static int CLASS_UNIQUE = 0x8000; - - final static int TYPE_IGNORE = 0; // This is a hack to stop further processing - public final static int TYPE_A = 1; // Address - final static int TYPE_NS = 2; // Name Server - final static int TYPE_MD = 3; // Mail Destination - final static int TYPE_MF = 4; // Mail Forwarder - final static int TYPE_CNAME = 5; // Canonical Name - final static int TYPE_SOA = 6; // Start of Authority - final static int TYPE_MB = 7; // Mailbox - final static int TYPE_MG = 8; // Mail Group - final static int TYPE_MR = 9; // Mail Rename - final static int TYPE_NULL = 10; // NULL RR - final static int TYPE_WKS = 11; // Well-known-service - final static int TYPE_PTR = 12; // Domain Name pofinal static inter - final static int TYPE_HINFO = 13; // Host information - final static int TYPE_MINFO = 14; // Mailbox information - final static int TYPE_MX = 15; // Mail exchanger - public final static int TYPE_TXT = 16;// Arbitrary text string - final static int TYPE_RP = 17; // for Responsible Person [RFC1183] - final static int TYPE_AFSDB = 18; // for AFS Data Base location [RFC1183] - final static int TYPE_X25 = 19; // for X.25 PSDN address [RFC1183] - final static int TYPE_ISDN = 20; // for ISDN address [RFC1183] - final static int TYPE_RT = 21; // for Route Through [RFC1183] - final static int TYPE_NSAP = 22; // for NSAP address, NSAP style A record [RFC1706] - final static int TYPE_NSAP_PTR = 23;// - final static int TYPE_SIG = 24; // for security signature [RFC2931] - final static int TYPE_KEY = 25; // for security key [RFC2535] - final static int TYPE_PX = 26; // X.400 mail mapping information [RFC2163] - final static int TYPE_GPOS = 27; // Geographical Position [RFC1712] - final static int TYPE_AAAA = 28; // IP6 Address [Thomson] - final static int TYPE_LOC = 29; // Location Information [Vixie] - final static int TYPE_NXT = 30; // Next Domain - OBSOLETE [RFC2535, RFC3755] - final static int TYPE_EID = 31; // Endpoint Identifier [Patton] - final static int TYPE_NIMLOC = 32; // Nimrod Locator [Patton] - public final static int TYPE_SRV = 33;// Server Selection [RFC2782] - final static int TYPE_ATMA = 34; // ATM Address [Dobrowski] - final static int TYPE_NAPTR = 35; // Naming Authority Pointer [RFC2168, RFC2915] - final static int TYPE_KX = 36; // Key Exchanger [RFC2230] - final static int TYPE_CERT = 37; // CERT [RFC2538] - final static int TYPE_A6 = 38; // A6 [RFC2874] - final static int TYPE_DNAME = 39; // DNAME [RFC2672] - final static int TYPE_SINK = 40; // SINK [Eastlake] - final static int TYPE_OPT = 41; // OPT [RFC2671] - final static int TYPE_APL = 42; // APL [RFC3123] - final static int TYPE_DS = 43; // Delegation Signer [RFC3658] - final static int TYPE_SSHFP = 44; // SSH Key Fingerprint [RFC-ietf-secsh-dns-05.txt] - final static int TYPE_RRSIG = 46; // RRSIG [RFC3755] - final static int TYPE_NSEC = 47; // NSEC [RFC3755] - final static int TYPE_DNSKEY = 48; // DNSKEY [RFC3755] - final static int TYPE_UINFO = 100; // [IANA-Reserved] - final static int TYPE_UID = 101; // [IANA-Reserved] - final static int TYPE_GID = 102; // [IANA-Reserved] - final static int TYPE_UNSPEC = 103; // [IANA-Reserved] - final static int TYPE_TKEY = 249; // Transaction Key [RFC2930] - final static int TYPE_TSIG = 250; // Transaction Signature [RFC2845] - final static int TYPE_IXFR = 251; // Incremental transfer [RFC1995] - final static int TYPE_AXFR = 252; // Transfer of an entire zone [RFC1035] - final static int TYPE_MAILA = 253; // Mailbox-related records (MB, MG or MR) [RFC1035] - final static int TYPE_MAILB = 254; // Mail agent RRs (Obsolete - see MX) [RFC1035] - final static int TYPE_ANY = 255; // Request for all records [RFC1035] - - //Time Intervals for various functions - - //milliseconds before send shared query - final static int SHARED_QUERY_TIME = 20; - //milliseconds between query loops. - final static int QUERY_WAIT_INTERVAL = 225; - //milliseconds between probe loops. - final static int PROBE_WAIT_INTERVAL = 250; - //minimal wait interval for response. - final static int RESPONSE_MIN_WAIT_INTERVAL = 20; - //maximal wait interval for response - final static int RESPONSE_MAX_WAIT_INTERVAL = 115; - //milliseconds to wait after conflict. - final static int PROBE_CONFLICT_INTERVAL = 1000; - //After x tries go 1 time a sec. on probes. - final static int PROBE_THROTTLE_COUNT = 10; - //We only increment the throttle count, if - // the previous increment is inside this interval. - final static int PROBE_THROTTLE_COUNT_INTERVAL = 5000; - //milliseconds between Announce loops. - final static int ANNOUNCE_WAIT_INTERVAL = 1000; - //milliseconds between cache cleanups. - final static int RECORD_REAPER_INTERVAL = 10000; - - final static int KNOWN_ANSWER_TTL = 120; - // 50% of the TTL in milliseconds - final static int ANNOUNCED_RENEWAL_TTL_INTERVAL = DNS_TTL * 500; -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java deleted file mode 100644 index 7132756..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.logging.*; - -/** - * DNS entry with a name, type, and class. This is the base - * class for questions and records. - * - * @version %I%, %G% - * @author Arthur van Hoff, Pierre Frisch, Rick Blair - * @author Christian Vincenot - */ -public class DNSEntry -{ - private static Logger logger = Logger.getLogger(DNSEntry.class.toString()); - String key; - String name; - int type; - int clazz; - boolean unique; - - /** - * Create an entry. - */ - DNSEntry(String name, int type, int clazz) - { - this.key = name.toLowerCase(); - this.name = name; - this.type = type; - this.clazz = clazz & DNSConstants.CLASS_MASK; - this.unique = (clazz & DNSConstants.CLASS_UNIQUE) != 0; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Check if two entries have exactly the same name, type, and class. - */ - @Override - public boolean equals(Object obj) - { - if (obj instanceof DNSEntry) - { - DNSEntry other = (DNSEntry) obj; - return name.equals(other.name) && - type == other.type && - clazz == other.clazz; - } - return false; - } - - public String getName() - { - return name; - } - - public int getType() - { - return type; - } - - public int getClazz() - { - return clazz; - } - - - public boolean isUnique() - { - return unique; - } - - /** - * Overriden, to return a value which is consistent with the value returned - * by equals(Object). - */ - @Override - public int hashCode() - { - return name.hashCode() + type + clazz; - } - - /** - * Get a string given a clazz. - */ - static String getClazz(int clazz) - { - switch (clazz & DNSConstants.CLASS_MASK) - { - case DNSConstants.CLASS_IN: - return "in"; - case DNSConstants.CLASS_CS: - return "cs"; - case DNSConstants.CLASS_CH: - return "ch"; - case DNSConstants.CLASS_HS: - return "hs"; - case DNSConstants.CLASS_NONE: - return "none"; - case DNSConstants.CLASS_ANY: - return "any"; - default: - return "?"; - } - } - - /** - * Get a string given a type. - */ - static String getType(int type) - { - switch (type) - { - case DNSConstants.TYPE_A: - return "a"; - case DNSConstants.TYPE_AAAA: - return "aaaa"; - case DNSConstants.TYPE_NS: - return "ns"; - case DNSConstants.TYPE_MD: - return "md"; - case DNSConstants.TYPE_MF: - return "mf"; - case DNSConstants.TYPE_CNAME: - return "cname"; - case DNSConstants.TYPE_SOA: - return "soa"; - case DNSConstants.TYPE_MB: - return "mb"; - case DNSConstants.TYPE_MG: - return "mg"; - case DNSConstants.TYPE_MR: - return "mr"; - case DNSConstants.TYPE_NULL: - return "null"; - case DNSConstants.TYPE_WKS: - return "wks"; - case DNSConstants.TYPE_PTR: - return "ptr"; - case DNSConstants.TYPE_HINFO: - return "hinfo"; - case DNSConstants.TYPE_MINFO: - return "minfo"; - case DNSConstants.TYPE_MX: - return "mx"; - case DNSConstants.TYPE_TXT: - return "txt"; - case DNSConstants.TYPE_SRV: - return "srv"; - case DNSConstants.TYPE_ANY: - return "any"; - default: - return "?"; - } - } - - public String toString(String hdr, String other) - { - return hdr + "[" + getType(type) + "," + - getClazz(clazz) + (unique ? "-unique," : ",") + - name + ((other != null) ? "," + - other + "]" : "]"); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java deleted file mode 100644 index 3dcedc4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.*; - -/** - * Parse an incoming DNS message into its components. - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch, Daniel Bobbert - */ -final class DNSIncoming -{ - private static Logger logger = Logger.getLogger(DNSIncoming.class.toString()); - // Implementation note: This vector should be immutable. - // If a client of DNSIncoming changes the contents of this vector, - // we get undesired results. To fix this, we have to migrate to - // the Collections API of Java 1.2. i.e we replace Vector by List. - // final static Vector EMPTY = new Vector(); - - private DatagramPacket packet; - private int off; - private int len; - private byte data[]; - - int id; - private int flags; - private int numQuestions; - int numAnswers; - private int numAuthorities; - private int numAdditionals; - private long receivedTime; - - List<DNSEntry> questions; - List<DNSRecord> answers; - - /** - * Parse a message from a datagram packet. - */ - DNSIncoming(DatagramPacket packet) throws IOException - { - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - - this.packet = packet; - this.data = packet.getData(); - this.len = packet.getLength(); - this.off = packet.getOffset(); - this.questions = new LinkedList<DNSEntry>(); - this.answers = new LinkedList<DNSRecord>(); - this.receivedTime = System.currentTimeMillis(); - - try - { - id = readUnsignedShort(); - flags = readUnsignedShort(); - numQuestions = readUnsignedShort(); - numAnswers = readUnsignedShort(); - numAuthorities = readUnsignedShort(); - numAdditionals = readUnsignedShort(); - - // parse questions - if (numQuestions > 0) - { - questions = - Collections.synchronizedList( - new ArrayList<DNSEntry>(numQuestions)); - for (int i = 0; i < numQuestions; i++) - { - DNSQuestion question = - new DNSQuestion( - readName(), - readUnsignedShort(), - readUnsignedShort()); - - questions.add(question); - } - } - - // parse answers - int n = numAnswers + numAuthorities + numAdditionals; - if (n > 0) - { - //System.out.println("JMDNS received "+n+" answers!"); - answers = Collections.synchronizedList( - new ArrayList<DNSRecord>(n)); - for (int i = 0; i < n; i++) - { - String domain = readName(); - int type = readUnsignedShort(); - int clazz = readUnsignedShort(); - int ttl = readInt(); - int len = readUnsignedShort(); - int end = off + len; - DNSRecord rec = null; - - switch (type) - { - case DNSConstants.TYPE_A: // IPv4 - case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested - rec = new DNSRecord.Address( - domain, type, clazz, ttl, readBytes(off, len)); - break; - case DNSConstants.TYPE_CNAME: - case DNSConstants.TYPE_PTR: - rec = new DNSRecord.Pointer( - domain, type, clazz, ttl, readName()); - break; - case DNSConstants.TYPE_TXT: - rec = new DNSRecord.Text( - domain, type, clazz, ttl, readBytes(off, len)); - break; - case DNSConstants.TYPE_SRV: - //System.out.println("JMDNS: One is a SRV field!!"); - rec = new DNSRecord.Service( domain, - type, - clazz, - ttl, - readUnsignedShort(), - readUnsignedShort(), - readUnsignedShort(), - readName()); - break; - case DNSConstants.TYPE_HINFO: - // Maybe we should do something with those - break; - default : - logger.finer("DNSIncoming() unknown type:" + type); - break; - } - - if (rec != null) - { - // Add a record, if we were able to create one. - answers.add(rec); - } - else - { - // Addjust the numbers for the skipped record - if (answers.size() < numAnswers) - { - numAnswers--; - } - else - { - if (answers.size() < numAnswers + numAuthorities) - { - numAuthorities--; - } - else - { - if (answers.size() < numAnswers + - numAuthorities + - numAdditionals) - { - numAdditionals--; - } - } - } - } - off = end; - } - } - } - catch (IOException e) - { - logger.log(Level.WARNING, - "DNSIncoming() dump " + print(true) + "\n exception ", e); - throw e; - } - } - - /** - * Check if the message is a query. - */ - boolean isQuery() - { - return (flags & DNSConstants.FLAGS_QR_MASK) == - DNSConstants.FLAGS_QR_QUERY; - } - - /** - * Check if the message is truncated. - */ - boolean isTruncated() - { - return (flags & DNSConstants.FLAGS_TC) != 0; - } - - /** - * Check if the message is a response. - */ - boolean isResponse() - { - return (flags & DNSConstants.FLAGS_QR_MASK) == - DNSConstants.FLAGS_QR_RESPONSE; - } - - private int get(int off) throws IOException - { - if ((off < 0) || (off >= len)) - { - throw new IOException("parser error: offset=" + off); - } - return data[off] & 0xFF; - } - - private int readUnsignedShort() throws IOException - { - return (get(off++) << 8) + get(off++); - } - - private int readInt() throws IOException - { - return (readUnsignedShort() << 16) + readUnsignedShort(); - } - - private byte[] readBytes(int off, int len) throws IOException - { - byte bytes[] = new byte[len]; - System.arraycopy(data, off, bytes, 0, len); - return bytes; - } - - private void readUTF(StringBuffer buf, int off, int len) throws IOException - { - for (int end = off + len; off < end;) - { - int ch = get(off++); - switch (ch >> 4) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - break; - case 12: - case 13: - // 110x xxxx 10xx xxxx - ch = ((ch & 0x1F) << 6) | (get(off++) & 0x3F); - break; - case 14: - // 1110 xxxx 10xx xxxx 10xx xxxx - ch = ((ch & 0x0f) << 12) | - ((get(off++) & 0x3F) << 6) | - (get(off++) & 0x3F); - break; - default: - // 10xx xxxx, 1111 xxxx - ch = ((ch & 0x3F) << 4) | (get(off++) & 0x0f); - break; - } - buf.append((char) ch); - } - } - - private String readName() throws IOException - { - StringBuffer buf = new StringBuffer(); - int off = this.off; - int next = -1; - int first = off; - - while (true) - { - int len = get(off++); - if (len == 0) - { - break; - } - switch (len & 0xC0) - { - case 0x00: - //buf.append("[" + off + "]"); - readUTF(buf, off, len); - off += len; - buf.append('.'); - break; - case 0xC0: - //buf.append("<" + (off - 1) + ">"); - if (next < 0) - { - next = off + 1; - } - off = ((len & 0x3F) << 8) | get(off++); - if (off >= first) - { - throw new IOException( - "bad domain name: possible circular name detected"); - } - first = off; - break; - default: - throw new IOException( - "bad domain name: '" + buf + "' at " + off); - } - } - this.off = (next >= 0) ? next : off; - return buf.toString(); - } - - /** - * Debugging. - */ - String print(boolean dump) - { - StringBuffer buf = new StringBuffer(); - buf.append(toString() + "\n"); - for (Iterator<DNSEntry> iterator = questions.iterator(); - iterator.hasNext();) - { - buf.append(" ques:" + iterator.next() + "\n"); - } - int count = 0; - for (Iterator<DNSRecord> iterator = answers.iterator(); - iterator.hasNext(); - count++) - { - if (count < numAnswers) - { - buf.append(" answ:"); - } - else - { - if (count < numAnswers + numAuthorities) - { - buf.append(" auth:"); - } - else - { - buf.append(" addi:"); - } - } - buf.append(iterator.next() + "\n"); - } - if (dump) - { - for (int off = 0, len = packet.getLength(); off < len; off += 32) - { - int n = Math.min(32, len - off); - if (off < 10) - { - buf.append(' '); - } - if (off < 100) - { - buf.append(' '); - } - buf.append(off); - buf.append(':'); - for (int i = 0; i < n; i++) - { - if ((i % 8) == 0) - { - buf.append(' '); - } - buf.append(Integer.toHexString((data[off + i] & 0xF0) >> 4)); - buf.append(Integer.toHexString((data[off + i] & 0x0F) >> 0)); - } - buf.append("\n"); - buf.append(" "); - for (int i = 0; i < n; i++) - { - if ((i % 8) == 0) - { - buf.append(' '); - } - buf.append(' '); - int ch = data[off + i] & 0xFF; - buf.append(((ch > ' ') && (ch < 127)) ? (char) ch : '.'); - } - buf.append("\n"); - - // limit message size - if (off + 32 >= 256) - { - buf.append("....\n"); - break; - } - } - } - return buf.toString(); - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append(isQuery() ? "dns[query," : "dns[response,"); - if (packet.getAddress() != null) - { - buf.append(packet.getAddress().getHostAddress()); - } - buf.append(':'); - buf.append(packet.getPort()); - buf.append(",len="); - buf.append(packet.getLength()); - buf.append(",id=0x"); - buf.append(Integer.toHexString(id)); - if (flags != 0) - { - buf.append(",flags=0x"); - buf.append(Integer.toHexString(flags)); - if ((flags & DNSConstants.FLAGS_QR_RESPONSE) != 0) - { - buf.append(":r"); - } - if ((flags & DNSConstants.FLAGS_AA) != 0) - { - buf.append(":aa"); - } - if ((flags & DNSConstants.FLAGS_TC) != 0) - { - buf.append(":tc"); - } - } - if (numQuestions > 0) - { - buf.append(",questions="); - buf.append(numQuestions); - } - if (numAnswers > 0) - { - buf.append(",answers="); - buf.append(numAnswers); - } - if (numAuthorities > 0) - { - buf.append(",authorities="); - buf.append(numAuthorities); - } - if (numAdditionals > 0) - { - buf.append(",additionals="); - buf.append(numAdditionals); - } - buf.append("]"); - return buf.toString(); - } - - /** - * Appends answers to this Incoming. - * - * @throws IllegalArgumentException If not a query or if Truncated. - */ - void append(DNSIncoming that) - { - if (this.isQuery() && this.isTruncated() && that.isQuery()) - { - if (that.numQuestions > 0) { - if (Collections.EMPTY_LIST.equals(this.questions)) - this.questions = - Collections.synchronizedList( - new ArrayList<DNSEntry>(that.numQuestions)); - - this.questions.addAll(that.questions); - this.numQuestions += that.numQuestions; - } - - if (Collections.EMPTY_LIST.equals(answers)) - { - answers = Collections.synchronizedList( - new ArrayList<DNSRecord>()); - } - - if (that.numAnswers > 0) - { - this.answers.addAll(this.numAnswers, - that.answers.subList(0, that.numAnswers)); - this.numAnswers += that.numAnswers; - } - if (that.numAuthorities > 0) - { - this.answers.addAll(this.numAnswers + this.numAuthorities, - that.answers.subList( - that.numAnswers, - that.numAnswers + that.numAuthorities)); - this.numAuthorities += that.numAuthorities; - } - if (that.numAdditionals > 0) - { - this.answers.addAll( - that.answers.subList( - that.numAnswers + that.numAuthorities, - that.numAnswers + that.numAuthorities + that.numAdditionals)); - this.numAdditionals += that.numAdditionals; - } - } - else - { - throw new IllegalArgumentException(); - } - } - - int elapseSinceArrival() - { - return (int) (System.currentTimeMillis() - receivedTime); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java deleted file mode 100644 index d317953..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -// REMIND: Listener should follow Java idiom for listener or have a different -// name. - -/** - * DNSListener. - * Listener for record updates. - * - * @author Werner Randelshofer, Rick Blair - * @version 1.0 May 22, 2004 Created. - */ -public interface DNSListener -{ - /** - * Update a DNS record. - * @param jmdns - * @param now - * @param record - */ - public void updateRecord(JmDNS jmdns, long now, DNSRecord record); -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java deleted file mode 100644 index 4d099b8..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.util.*; -import java.util.logging.*; - -/** - * An outgoing DNS message. - * - * @version %I%, %G% - * @author Arthur van Hoff, Rick Blair, Werner Randelshofer - */ -final class DNSOutgoing -{ - private static Logger logger = - Logger.getLogger(DNSOutgoing.class.toString()); - - int id; - int flags; - private boolean multicast; - private int numQuestions; - private int numAnswers; - private int numAuthorities; - private int numAdditionals; - private Hashtable<String, Integer> names; - - byte data[]; - int off; - int len; - - /** - * Create an outgoing multicast query or response. - */ - DNSOutgoing(int flags) - { - this(flags, true); - - } - - /** - * Create an outgoing query or response. - */ - DNSOutgoing(int flags, boolean multicast) - { - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - - this.flags = flags; - this.multicast = multicast; - names = new Hashtable<String, Integer>(); - data = new byte[DNSConstants.MAX_MSG_TYPICAL]; - off = 12; - } - - /** - * Add a question to the message. - */ - void addQuestion(DNSQuestion rec) throws IOException - { - if (numAnswers > 0 || numAuthorities > 0 || numAdditionals > 0) - { - throw new IllegalStateException("Questions must be added before answers"); - } - numQuestions++; - writeQuestion(rec); - } - - /** - * Add an answer if it is not suppressed. - */ - void addAnswer(DNSIncoming in, DNSRecord rec) throws IOException - { - if (numAuthorities > 0 || numAdditionals > 0) - { - throw new IllegalStateException( - "Answers must be added before authorities and additionals"); - } - if (!rec.suppressedBy(in)) - { - addAnswer(rec, 0); - } - } - - /** - * Add an additional answer to the record. Omit if there is no room. - */ - void addAdditionalAnswer(DNSIncoming in, DNSRecord rec) throws IOException - { - if ((off < DNSConstants.MAX_MSG_TYPICAL - 200) && !rec.suppressedBy(in)) - { - writeRecord(rec, 0); - numAdditionals++; - } - } - - /** - * Add an answer to the message. - */ - void addAnswer(DNSRecord rec, long now) throws IOException - { - if (numAuthorities > 0 || numAdditionals > 0) - { - throw new IllegalStateException( - "Questions must be added before answers"); - } - if (rec != null) - { - if ((now == 0) || !rec.isExpired(now)) - { - writeRecord(rec, now); - numAnswers++; - } - } - } - - private LinkedList<DNSRecord> authorativeAnswers = new LinkedList<DNSRecord>(); - - /** - * Add an authorative answer to the message. - */ - void addAuthorativeAnswer(DNSRecord rec) throws IOException - { - if (numAdditionals > 0) - { - throw new IllegalStateException( - "Authorative answers must be added before additional answers"); - } - authorativeAnswers.add(rec); - writeRecord(rec, 0); - numAuthorities++; - - // VERIFY: - - } - - void writeByte(int value) throws IOException - { - if (off >= data.length) - { - throw new IOException("buffer full"); - } - data[off++] = (byte) value; - } - - void writeBytes(String str, int off, int len) throws IOException - { - for (int i = 0; i < len; i++) - { - writeByte(str.charAt(off + i)); - } - } - - void writeBytes(byte data[]) throws IOException - { - if (data != null) - { - writeBytes(data, 0, data.length); - } - } - - void writeBytes(byte data[], int off, int len) throws IOException - { - for (int i = 0; i < len; i++) - { - writeByte(data[off + i]); - } - } - - void writeShort(int value) throws IOException - { - writeByte(value >> 8); - writeByte(value); - } - - void writeInt(int value) throws IOException - { - writeShort(value >> 16); - writeShort(value); - } - - void writeUTF(String str, int off, int len) throws IOException - { - // compute utf length - int utflen = 0; - for (int i = 0; i < len; i++) - { - int ch = str.charAt(off + i); - if ((ch >= 0x0001) && (ch <= 0x007F)) - { - utflen += 1; - } - else - { - if (ch > 0x07FF) - { - utflen += 3; - } - else - { - utflen += 2; - } - } - } - // write utf length - writeByte(utflen); - // write utf data - for (int i = 0; i < len; i++) - { - int ch = str.charAt(off + i); - if ((ch >= 0x0001) && (ch <= 0x007F)) - { - writeByte(ch); - } - else - { - if (ch > 0x07FF) - { - writeByte(0xE0 | ((ch >> 12) & 0x0F)); - writeByte(0x80 | ((ch >> 6) & 0x3F)); - writeByte(0x80 | ((ch >> 0) & 0x3F)); - } - else - { - writeByte(0xC0 | ((ch >> 6) & 0x1F)); - writeByte(0x80 | ((ch >> 0) & 0x3F)); - } - } - } - } - - void writeName(String name) throws IOException - { - while (true) - { - int n = name.indexOf('.'); - if (n < 0) - { - n = name.length(); - } - if (n <= 0) - { - writeByte(0); - return; - } - Integer offset = names.get(name); - if (offset != null) - { - int val = offset.intValue(); - - if (val > off) - { - logger.log(Level.WARNING, - "DNSOutgoing writeName failed val=" + val + " name=" + name); - } - - writeByte((val >> 8) | 0xC0); - writeByte(val); - return; - } - names.put(name, off); - writeUTF(name, 0, n); - name = name.substring(n); - if (name.startsWith(".")) - { - name = name.substring(1); - } - } - } - - void writeQuestion(DNSQuestion question) throws IOException - { - writeName(question.name); - writeShort(question.type); - writeShort(question.clazz); - } - - void writeRecord(DNSRecord rec, long now) throws IOException - { - int save = off; - try - { - writeName(rec.name); - writeShort(rec.type); - writeShort(rec.clazz | - ((rec.unique && multicast) ? DNSConstants.CLASS_UNIQUE : 0)); - writeInt((now == 0) ? rec.ttl : rec.getRemainingTTL(now)); - writeShort(0); - int start = off; - rec.write(this); - int len = off - start; - data[start - 2] = (byte) (len >> 8); - data[start - 1] = (byte) (len & 0xFF); - } - catch (IOException e) - { - off = save; - throw e; - } - } - - /** - * Finish the message before sending it off. - */ - void finish() throws IOException - { - int save = off; - off = 0; - - writeShort(multicast ? 0 : id); - writeShort(flags); - writeShort(numQuestions); - writeShort(numAnswers); - writeShort(numAuthorities); - writeShort(numAdditionals); - off = save; - } - - boolean isQuery() - { - return (flags & DNSConstants.FLAGS_QR_MASK) == - DNSConstants.FLAGS_QR_QUERY; - } - - public boolean isEmpty() - { - return numQuestions == 0 && numAuthorities == 0 - && numAdditionals == 0 && numAnswers == 0; - } - - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append(isQuery() ? "dns[query," : "dns[response,"); - //buf.append(packet.getAddress().getHostAddress()); - buf.append(':'); - //buf.append(packet.getPort()); - //buf.append(",len="); - //buf.append(packet.getLength()); - buf.append(",id=0x"); - buf.append(Integer.toHexString(id)); - if (flags != 0) - { - buf.append(",flags=0x"); - buf.append(Integer.toHexString(flags)); - if ((flags & DNSConstants.FLAGS_QR_RESPONSE) != 0) - { - buf.append(":r"); - } - if ((flags & DNSConstants.FLAGS_AA) != 0) - { - buf.append(":aa"); - } - if ((flags & DNSConstants.FLAGS_TC) != 0) - { - buf.append(":tc"); - } - } - if (numQuestions > 0) - { - buf.append(",questions="); - buf.append(numQuestions); - } - if (numAnswers > 0) - { - buf.append(",answers="); - buf.append(numAnswers); - } - if (numAuthorities > 0) - { - buf.append(",authorities="); - buf.append(numAuthorities); - } - if (numAdditionals > 0) - { - buf.append(",additionals="); - buf.append(numAdditionals); - } - buf.append(",\nnames=" + names); - buf.append(",\nauthorativeAnswers=" + authorativeAnswers); - - buf.append("]"); - return buf.toString(); - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java deleted file mode 100644 index f6abaa7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.logging.*; - -/** - * A DNS question. - * - * @version %I%, %G% - * @author Arthur van Hoff - */ -public final class DNSQuestion - extends DNSEntry -{ - private static Logger logger = - Logger.getLogger(DNSQuestion.class.toString()); - - /** - * Create a question. - * @param name - * @param type - * @param clazz - */ - public DNSQuestion(String name, int type, int clazz) - { - super(name, type, clazz); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Check if this question is answered by a given DNS record. - */ - boolean answeredBy(DNSRecord rec) - { - return (clazz == rec.clazz) && - ((type == rec.type) || - (type == DNSConstants.TYPE_ANY)) && - name.equals(rec.name); - } - - /** - * For debugging only. - */ - @Override - public String toString() - { - return toString("question", null); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java deleted file mode 100644 index 673bbc4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java +++ /dev/null @@ -1,796 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.logging.*; - -/** - * DNS record - * - * @version %I%, %G% - * @author Arthur van Hoff, Rick Blair, Werner Randelshofer, Pierre Frisch - */ -public abstract class DNSRecord extends DNSEntry -{ - private static Logger logger = - Logger.getLogger(DNSRecord.class.toString()); - int ttl; - private long created; - - /** - * Create a DNSRecord with a name, type, clazz, and ttl. - */ - DNSRecord(String name, int type, int clazz, int ttl) - { - super(name, type, clazz); - this.ttl = ttl; - this.created = System.currentTimeMillis(); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * True if this record is the same as some other record. - * @param other obj to be compared to. - */ - @Override - public boolean equals(Object other) - { - return (other instanceof DNSRecord) && sameAs((DNSRecord) other); - } - - /** - * True if this record is the same as some other record. - */ - boolean sameAs(DNSRecord other) - { - return super.equals(other) && sameValue(other); - } - - /** - * True if this record has the same value as some other record. - */ - abstract boolean sameValue(DNSRecord other); - - /** - * True if this record has the same type as some other record. - */ - boolean sameType(DNSRecord other) - { - return type == other.type; - } - - /** - * Handles a query represented by this record. - * - * @return Returns true if a conflict with one of the services registered - * with JmDNS or with the hostname occured. - */ - abstract boolean handleQuery(JmDNS dns, long expirationTime); - - /** - * Handles a responserepresented by this record. - * - * @return Returns true if a conflict with one of the services registered - * with JmDNS or with the hostname occured. - */ - abstract boolean handleResponse(JmDNS dns); - - /** - * Adds this as an answer to the provided outgoing datagram. - */ - abstract DNSOutgoing addAnswer(JmDNS dns, DNSIncoming in, InetAddress addr, - int port, DNSOutgoing out) - throws IOException; - - /** - * True if this record is suppressed by the answers in a message. - */ - boolean suppressedBy(DNSIncoming msg) - { - try - { - for (int i = msg.numAnswers; i-- > 0;) - { - if (suppressedBy(msg.answers.get(i))) - { - return true; - } - } - return false; - } - catch (ArrayIndexOutOfBoundsException e) - { - logger.log(Level.WARNING, - "suppressedBy() message " + msg + " exception ", e); - // msg.print(true); - return false; - } - } - - /** - * True if this record would be supressed by an answer. - * This is the case if this record would not have a - * significantly longer TTL. - */ - boolean suppressedBy(DNSRecord other) - { - if (sameAs(other) && (other.ttl > ttl / 2)) - { - return true; - } - return false; - } - - /** - * Get the expiration time of this record. - */ - long getExpirationTime(int percent) - { - return created + (percent * ttl * 10L); - } - - /** - * Get the remaining TTL for this record. - */ - int getRemainingTTL(long now) - { - return (int) Math.max(0, (getExpirationTime(100) - now) / 1000); - } - - /** - * Check if the record is expired. - */ - boolean isExpired(long now) - { - return getExpirationTime(100) <= now; - } - - /** - * Check if the record is stale, ie it has outlived - * more than half of its TTL. - */ - boolean isStale(long now) - { - return getExpirationTime(50) <= now; - } - - /** - * Reset the TTL of a record. This avoids having to - * update the entire record in the cache. - */ - void resetTTL(DNSRecord other) - { - created = other.created; - ttl = other.ttl; - } - - /** - * Write this record into an outgoing message. - */ - abstract void write(DNSOutgoing out) throws IOException; - - /** - * Address record. - */ - static class Address extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Address.class.toString()); - InetAddress addr; - - Address(String name, int type, int clazz, int ttl, InetAddress addr) - { - super(name, type, clazz, ttl); - this.addr = addr; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - Address(String name, int type, int clazz, int ttl, byte[] rawAddress) - { - super(name, type, clazz, ttl); - try - { - this.addr = InetAddress.getByAddress(rawAddress); - } - catch (UnknownHostException exception) - { - logger.log(Level.WARNING, "Address() exception ", exception); - } - } - - @Override - void write(DNSOutgoing out) throws IOException - { - if (addr != null) - { - byte[] buffer = addr.getAddress(); - if (DNSConstants.TYPE_A == type) - { - // If we have a type A records we should - // answer with a IPv4 address - if (addr instanceof Inet4Address) - { - // All is good - } - else - { - // Get the last four bytes - byte[] tempbuffer = buffer; - buffer = new byte[4]; - System.arraycopy(tempbuffer, 12, buffer, 0, 4); - } - } - else - { - // If we have a type AAAA records we should - // answer with a IPv6 address - if (addr instanceof Inet4Address) - { - byte[] tempbuffer = buffer; - buffer = new byte[16]; - for (int i = 0; i < 16; i++) - { - if (i < 11) - { - buffer[i] = tempbuffer[i - 12]; - } - else - { - buffer[i] = 0; - } - } - } - } - int length = buffer.length; - out.writeBytes(buffer, 0, length); - } - } - - boolean same(DNSRecord other) - { - return ((sameName(other)) && ((sameValue(other)))); - } - - boolean sameName(DNSRecord other) - { - return name.equalsIgnoreCase(((Address) other).name); - } - - @Override - boolean sameValue(DNSRecord other) - { - return addr.equals(((Address) other).getAddress()); - } - - InetAddress getAddress() - { - return addr; - } - - /** - * Creates a byte array representation of this record. - * This is needed for tie-break tests according to - * draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2. - */ - private byte[] toByteArray() - { - try - { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream dout = new DataOutputStream(bout); - dout.write(name.getBytes("UTF8")); - dout.writeShort(type); - dout.writeShort(clazz); - //dout.writeInt(len); - byte[] buffer = addr.getAddress(); - for (int i = 0; i < buffer.length; i++) - { - dout.writeByte(buffer[i]); - } - dout.close(); - return bout.toByteArray(); - } - catch (IOException e) - { - throw new InternalError(); - } - } - - /** - * Does a lexicographic comparison of the byte array representation - * of this record and that record. - * This is needed for tie-break tests according to - * draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2. - */ - private int lexCompare(DNSRecord.Address that) - { - byte[] thisBytes = this.toByteArray(); - byte[] thatBytes = that.toByteArray(); - for ( int i = 0, n = Math.min(thisBytes.length, thatBytes.length); - i < n; - i++) - { - if (thisBytes[i] > thatBytes[i]) - { - return 1; - } - else - { - if (thisBytes[i] < thatBytes[i]) - { - return -1; - } - } - } - return thisBytes.length - thatBytes.length; - } - - /** - * Does the necessary actions, when this as a query. - */ - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - DNSRecord.Address dnsAddress = - dns.getLocalHost().getDNSAddressRecord(this); - if (dnsAddress != null) - { - if (dnsAddress.sameType(this) && - dnsAddress.sameName(this) && - (!dnsAddress.sameValue(this))) - { - logger.finer( - "handleQuery() Conflicting probe detected. dns state " + - dns.getState() + - " lex compare " + lexCompare(dnsAddress)); - // Tie-breaker test - if (dns.getState().isProbing() && lexCompare(dnsAddress) >= 0) - { - // We lost the tie-break. We have to choose a different name. - dns.getLocalHost().incrementHostName(); - dns.getCache().clear(); - for (ServiceInfo info : dns.services.values()) - info.revertState(); - } - dns.revertState(); - return true; - } - } - return false; - } - - /** - * Does the necessary actions, when this as a response. - */ - @Override - boolean handleResponse(JmDNS dns) - { - DNSRecord.Address dnsAddress = - dns.getLocalHost().getDNSAddressRecord(this); - if (dnsAddress != null) - { - if (dnsAddress.sameType(this) && - dnsAddress.sameName(this) && - (!dnsAddress.sameValue(this))) - { - logger.finer("handleResponse() Denial detected"); - - if (dns.getState().isProbing()) - { - dns.getLocalHost().incrementHostName(); - dns.getCache().clear(); - for (ServiceInfo info : dns.services.values()) - info.revertState(); - } - dns.revertState(); - return true; - } - } - return false; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - return out; - } - - @Override - public String toString() - { - return toString(" address '" + - (addr != null ? addr.getHostAddress() : "null") + "'"); - } - - } - - /** - * Pointer record. - */ - static class Pointer extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Pointer.class.toString()); - String alias; - - Pointer(String name, int type, int clazz, int ttl, String alias) - { - super(name, type, clazz, ttl); - this.alias = alias; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - void write(DNSOutgoing out) throws IOException - { - out.writeName(alias); - } - - @Override - boolean sameValue(DNSRecord other) - { - return alias.equals(((Pointer) other).alias); - } - - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - // Nothing to do (?) - // I think there is no possibility - // for conflicts for this record type? - return false; - } - - @Override - boolean handleResponse(JmDNS dns) - { - // Nothing to do (?) - // I think there is no possibility for conflicts for this record type? - return false; - } - - String getAlias() - { - return alias; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - return out; - } - - @Override - public String toString() - { - return toString(alias); - } - } - - static class Text extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Text.class.toString()); - byte text[]; - - Text(String name, int type, int clazz, int ttl, byte text[]) - { - super(name, type, clazz, ttl); - this.text = text; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - void write(DNSOutgoing out) throws IOException - { - out.writeBytes(text, 0, text.length); - } - - @Override - boolean sameValue(DNSRecord other) - { - Text txt = (Text) other; - if (txt.text.length != text.length) - { - return false; - } - for (int i = text.length; i-- > 0;) - { - if (txt.text[i] != text[i]) - { - return false; - } - } - return true; - } - - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - // Nothing to do (?) - // I think there is no possibility for conflicts for this record type? - return false; - } - - @Override - boolean handleResponse(JmDNS dns) - { - // Nothing to do (?) - // Shouldn't we care if we get a conflict at this level? - /* - ServiceInfo info = (ServiceInfo) dns.services.get(name.toLowerCase()); - if (info != null) - { - if (! Arrays.equals(text,info.text)) - { - info.revertState(); - return true; - } - } - */ - return false; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - return out; - } - - @Override - public String toString() - { - return toString((text.length > 10) ? - new String(text, 0, 7) + "..." : - new String(text)); - } - } - - /** - * Service record. - */ - static class Service extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Service.class.toString()); - int priority; - int weight; - int port; - String server; - - Service(String name, - int type, - int clazz, - int ttl, - int priority, - int weight, - int port, - String server) - { - super(name, type, clazz, ttl); - this.priority = priority; - this.weight = weight; - this.port = port; - this.server = server; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - void write(DNSOutgoing out) throws IOException - { - out.writeShort(priority); - out.writeShort(weight); - out.writeShort(port); - out.writeName(server); - } - - private byte[] toByteArray() - { - try - { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream dout = new DataOutputStream(bout); - dout.write(name.getBytes("UTF8")); - dout.writeShort(type); - dout.writeShort(clazz); - //dout.writeInt(len); - dout.writeShort(priority); - dout.writeShort(weight); - dout.writeShort(port); - dout.write(server.getBytes("UTF8")); - dout.close(); - return bout.toByteArray(); - } - catch (IOException e) - { - throw new InternalError(); - } - } - - private int lexCompare(DNSRecord.Service that) - { - byte[] thisBytes = this.toByteArray(); - byte[] thatBytes = that.toByteArray(); - for (int i = 0, n = Math.min(thisBytes.length, thatBytes.length); - i < n; - i++) - { - if (thisBytes[i] > thatBytes[i]) - { - return 1; - } - else - { - if (thisBytes[i] < thatBytes[i]) - { - return -1; - } - } - } - return thisBytes.length - thatBytes.length; - } - - @Override - boolean sameValue(DNSRecord other) - { - Service s = (Service) other; - return (priority == s.priority) && - (weight == s.weight) && - (port == s.port) && - server.equals(s.server); - } - - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - ServiceInfo info = dns.services.get(name.toLowerCase()); - if (info != null && - (port != info.port || - !server.equalsIgnoreCase(dns.getLocalHost().getName()))) - { - logger.finer("handleQuery() Conflicting probe detected"); - - // Tie breaker test - if (info.getState().isProbing() && - lexCompare(new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - dns.getLocalHost().getName())) >= 0) - { - // We lost the tie break - String oldName = info.getQualifiedName().toLowerCase(); - info.setName(dns.incrementName(info.getName())); - dns.services.remove(oldName); - dns.services.put(info.getQualifiedName().toLowerCase(), info); - logger.finer( - "handleQuery() Lost tie break: new unique name chosen:" + info.getName()); - - } - info.revertState(); - return true; - - } - return false; - } - - @Override - boolean handleResponse(JmDNS dns) - { - ServiceInfo info = dns.services.get(name.toLowerCase()); - if (info != null && - (port != info.port || !server.equalsIgnoreCase(dns.getLocalHost().getName()))) - { - logger.finer("handleResponse() Denial detected"); - - if (info.getState().isProbing()) - { - String oldName = info.getQualifiedName().toLowerCase(); - info.setName(dns.incrementName(info.getName())); - dns.services.remove(oldName); - dns.services.put(info.getQualifiedName().toLowerCase(), info); - logger.finer( - "handleResponse() New unique name chose:" + info.getName()); - - } - info.revertState(); - return true; - } - return false; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - ServiceInfo info = dns.services.get(name.toLowerCase()); - if (info != null) - { - if (this.port == info.port != server.equals(dns.getLocalHost().getName())) - { - return dns.addAnswer(in, addr, port, out, - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - dns.getLocalHost().getName())); - } - } - return out; - } - - @Override - public String toString() - { - return toString(server + ":" + port); - } - } - - public String toString(String other) - { - return toString("record", ttl + "/" + - getRemainingTTL(System.currentTimeMillis()) -// + "," + other - ); - } -} - diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java deleted file mode 100644 index 5016050..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; -import java.util.logging.*; - -/** - * DNSState defines the possible states for services registered with JmDNS. - * - * @author Werner Randelshofer, Rick Blair - * @version 1.0 May 23, 2004 Created. - */ -public class DNSState - implements Comparable<DNSState> -{ - private static Logger logger = - Logger.getLogger(DNSState.class.toString()); - - private final String name; - - /** - * Ordinal of next state to be created. - */ - private static int nextOrdinal = 0; - /** - * Assign an ordinal to this state. - */ - private final int ordinal = nextOrdinal++; - /** - * Logical sequence of states. - * The sequence is consistent with the ordinal of a state. - * This is used for advancing through states. - */ - private final static ArrayList<DNSState> sequence - = new ArrayList<DNSState>(); - - private DNSState(String name) - { - this.name = name; - sequence.add(this); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - public final String toString() - { - return name; - } - - public static final DNSState PROBING_1 = new DNSState("probing 1"); - public static final DNSState PROBING_2 = new DNSState("probing 2"); - public static final DNSState PROBING_3 = new DNSState("probing 3"); - public static final DNSState ANNOUNCING_1 = new DNSState("announcing 1"); - public static final DNSState ANNOUNCING_2 = new DNSState("announcing 2"); - public static final DNSState ANNOUNCED = new DNSState("announced"); - public static final DNSState CANCELED = new DNSState("canceled"); - - /** - * Returns the next advanced state. - * In general, this advances one step in the following sequence: PROBING_1, - * PROBING_2, PROBING_3, ANNOUNCING_1, ANNOUNCING_2, ANNOUNCED. - * Does not advance for ANNOUNCED and CANCELED state. - * @return Returns the next advanced state. - */ - public final DNSState advance() - { - return (isProbing() || isAnnouncing()) ? - sequence.get(ordinal + 1) : - this; - } - - /** - * Returns to the next reverted state. - * All states except CANCELED revert to PROBING_1. - * Status CANCELED does not revert. - * @return Returns to the next reverted state. - */ - public final DNSState revert() - { - return (this == CANCELED) ? this : PROBING_1; - } - - /** - * Returns true, if this is a probing state. - * @return Returns true, if this is a probing state. - */ - public boolean isProbing() - { - return compareTo(PROBING_1) >= 0 && compareTo(PROBING_3) <= 0; - } - - /** - * Returns true, if this is an announcing state. - * @return Returns true, if this is an announcing state. - */ - public boolean isAnnouncing() - { - return compareTo(ANNOUNCING_1) >= 0 && compareTo(ANNOUNCING_2) <= 0; - } - - /** - * Returns true, if this is an announced state. - * @return Returns true, if this is an announced state. - */ - public boolean isAnnounced() - { - return compareTo(ANNOUNCED) == 0; - } - - /** - * Compares two states. - * The states compare as follows: - * PROBING_1 < PROBING_2 < PROBING_3 < ANNOUNCING_1 < - * ANNOUNCING_2 < RESPONDING < ANNOUNCED < CANCELED. - */ - public int compareTo(DNSState state) - { - return ordinal - state.ordinal; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java deleted file mode 100644 index 644afc9..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.net.*; -import java.util.logging.*; - - -/** - * HostInfo information on the local host to be able to cope with change of addresses. - * - * @version %I%, %G% - * @author Pierre Frisch, Werner Randelshofer - */ -class HostInfo -{ - private static Logger logger = Logger.getLogger(HostInfo.class.toString()); - protected String name; - protected InetAddress address; - protected NetworkInterface interfaze; - /** - * This is used to create a unique name for the host name. - */ - private int hostNameCount; - - public HostInfo(InetAddress address, String name) - { - super(); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - - this.address = address; - this.name = name; - if (address != null) - { - try - { - interfaze = NetworkInterface.getByInetAddress(address); - } - catch (Exception exception) - { - // FIXME Shouldn't we take an action here? - logger.log(Level.WARNING, - "LocalHostInfo() exception ", exception); - } - } - } - - public String getName() - { - return name; - } - - public InetAddress getAddress() - { - return address; - } - - public NetworkInterface getInterface() - { - return interfaze; - } - - synchronized String incrementHostName() - { - hostNameCount++; - int plocal = name.indexOf(".local."); - int punder = name.lastIndexOf("-"); - name = name.substring(0, (punder == -1 ? plocal : punder)) + "-" + - hostNameCount + ".local."; - return name; - } - - boolean shouldIgnorePacket(DatagramPacket packet) - { - boolean result = false; - if (getAddress() != null) - { - InetAddress from = packet.getAddress(); - if (from != null) - { - if (from.isLinkLocalAddress() && - (!getAddress().isLinkLocalAddress())) - { - // Ignore linklocal packets on regular interfaces, unless this is - // also a linklocal interface. This is to avoid duplicates. This is - // a terrible hack caused by the lack of an API to get the address - // of the interface on which the packet was received. - result = true; - } - if (from.isLoopbackAddress() && - (!getAddress().isLoopbackAddress())) - { - // Ignore loopback packets on a regular interface unless this is - // also a loopback interface. - result = true; - } - } - } - return result; - } - - DNSRecord.Address getDNSAddressRecord(DNSRecord.Address address) - { - return (DNSConstants.TYPE_AAAA == address.type ? - getDNS6AddressRecord() : - getDNS4AddressRecord()); - } - - DNSRecord.Address getDNS4AddressRecord() - { - if ((getAddress() != null) && - ((getAddress() instanceof Inet4Address) || - ((getAddress() instanceof Inet6Address) && - (((Inet6Address) getAddress()).isIPv4CompatibleAddress())))) - { - return new DNSRecord.Address(getName(), - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, getAddress()); - } - return null; - } - - DNSRecord.Address getDNS6AddressRecord() - { - if ((getAddress() != null) && (getAddress() instanceof Inet6Address)) - { - return new DNSRecord.Address( - getName(), - DNSConstants.TYPE_AAAA, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - getAddress()); - } - return null; - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append("local host info["); - buf.append(getName() != null ? getName() : "no name"); - buf.append(", "); - buf.append(getInterface() != null ? - getInterface().getDisplayName() : - "???"); - buf.append(":"); - buf.append(getAddress() != null ? - getAddress().getHostAddress() : - "no address"); - buf.append("]"); - return buf.toString(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java deleted file mode 100644 index 96420ba..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java +++ /dev/null @@ -1,3048 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.*; - -import net.java.sip.communicator.util.*; - -// REMIND: multiple IP addresses - -/** - * mDNS implementation in Java. - * - * @version %I%, %G% - * @author Arthur van Hoff, Rick Blair, Jeff Sonstein, - * Werner Randelshofer, Pierre Frisch, Scott Lewis - * @author Christian Vincenot - */ -public class JmDNS -{ - private static final Logger logger - = Logger.getLogger(JmDNS.class); - - /** - * The version of JmDNS. - */ - public static String VERSION = "2.0"; - - /** - * This is the multicast group, we are listening to for - * multicast DNS messages. - */ - private InetAddress group; - /** - * This is our multicast socket. - */ - private MulticastSocket socket; - - /** - * Used to fix live lock problem on unregester. - */ - - protected boolean closed = false; - - /** - * Holds instances of JmDNS.DNSListener. - * Must by a synchronized collection, because it is updated from - * concurrent threads. - */ - private List<DNSListener> listeners; - /** - * Holds instances of ServiceListener's. - * Keys are Strings holding a fully qualified service type. - * Values are LinkedList's of ServiceListener's. - */ - private Map<String, List<ServiceListener>> serviceListeners; - /** - * Holds instances of ServiceTypeListener's. - */ - private List<ServiceTypeListener> typeListeners; - - - /** - * Cache for DNSEntry's. - */ - private DNSCache cache; - - /** - * This hashtable holds the services that have been registered. - * Keys are instances of String which hold an all lower-case version of the - * fully qualified service name. - * Values are instances of ServiceInfo. - */ - Map<String, ServiceInfo> services; - - /** - * This hashtable holds the service types that have been registered or - * that have been received in an incoming datagram. - * Keys are instances of String which hold an all lower-case version of the - * fully qualified service type. - * Values hold the fully qualified service type. - */ - Map<String, String> serviceTypes; - - /** - * Handle on the local host - */ - HostInfo localHost; - - private Thread incomingListener = null; - - /** - * Throttle count. - * This is used to count the overall number of probes sent by JmDNS. - * When the last throttle increment happened . - */ - private int throttle; - /** - * Last throttle increment. - */ - private long lastThrottleIncrement; - - /** - * The timer is used to dispatch all outgoing messages of JmDNS. - * It is also used to dispatch maintenance tasks for the DNS cache. - */ - private Timer timer; - - /** - * The source for random values. - * This is used to introduce random delays in responses. This reduces the - * potential for collisions on the network. - */ - private final static Random random = new Random(); - - /** - * This lock is used to coordinate processing of incoming and outgoing - * messages. This is needed, because the Rendezvous Conformance Test - * does not forgive race conditions. - */ - private Object ioLock = new Object(); - - /** - * If an incoming package which needs an answer is truncated, we store it - * here. We add more incoming DNSRecords to it, until the JmDNS.Responder - * timer picks it up. - * Remind: This does not work well with multiple planned answers for packages - * that came in from different clients. - */ - private DNSIncoming plannedAnswer; - - // State machine - /** - * The state of JmDNS. - * <p/> - * For proper handling of concurrency, this variable must be - * changed only using methods advanceState(), revertState() and cancel(). - */ - private DNSState state = DNSState.PROBING_1; - - /** - * Timer task associated to the host name. - * This is used to prevent from having multiple tasks associated to the host - * name at the same time. - */ - TimerTask task; - - /** - * This hashtable is used to maintain a list of service types being collected - * by this JmDNS instance. - * The key of the hashtable is a service type name, the value is an instance - * of JmDNS.ServiceCollector. - * - * @see #list - */ - private HashMap<String, ServiceCollector> serviceCollectors = new HashMap<String, ServiceCollector>(); - - /** - * Create an instance of JmDNS. - * @throws java.io.IOException - */ - public JmDNS() - throws IOException - { - //String SLevel = System.getProperty("jmdns.debug"); - - if (logger.isDebugEnabled()) - logger.debug("JmDNS instance created"); - try - { - InetAddress addr = InetAddress.getLocalHost(); - // [PJYF Oct 14 2004] Why do we disallow the loopback address? - init(addr.isLoopbackAddress() ? null : addr, addr.getHostName()); - } - catch (IOException exc) - { - logger.error("Failed to get a reference to localhost", exc); - init(null, "computer"); - } - } - - /** - * Create an instance of JmDNS and bind it to a - * specific network interface given its IP-address. - * @param addr - * @throws java.io.IOException - */ - public JmDNS(InetAddress addr) - throws IOException - { - try - { - init(addr, addr.getHostName()); - } - catch (IOException e) - { - init(null, "computer"); - } - } - - /** - * Initialize everything. - * - * @param address The interface to which JmDNS binds to. - * @param name The host name of the interface. - */ - private void init(InetAddress address, String name) throws IOException - { - // A host name with "." is illegal. - // so strip off everything and append .local. - int idx = name.indexOf("."); - if (idx > 0) - { - name = name.substring(0, idx); - } - name += ".local."; - // localHost to IP address binding - localHost = new HostInfo(address, name); - - cache = new DNSCache(100); - - listeners = Collections.synchronizedList(new ArrayList<DNSListener>()); - serviceListeners = new HashMap<String, List<ServiceListener>>(); - typeListeners = new ArrayList<ServiceTypeListener>(); - - services = new Hashtable<String, ServiceInfo>(20); - serviceTypes = new Hashtable<String, String>(20); - - // REMIND: If I could pass in a name for the Timer thread, - // I would pass 'JmDNS.Timer'. - timer = new Timer(); - new RecordReaper().start(); - - incomingListener = new Thread( - new SocketListener(), "JmDNS.SocketListener"); - incomingListener.setDaemon(true); - // Bind to multicast socket - openMulticastSocket(localHost); - start(services.values()); - } - - private void start(Collection<ServiceInfo> serviceInfos) - { - state = DNSState.PROBING_1; - incomingListener.start(); - new Prober().start(); - for (ServiceInfo serviceInfo : serviceInfos) - { - try - { - registerService(new ServiceInfo(serviceInfo)); - } - catch (Exception exception) - { - logger.warn("start() Registration exception ", exception); - } - } - } - - private void openMulticastSocket(HostInfo hostInfo) throws IOException - { - if (group == null) - { - group = InetAddress.getByName(DNSConstants.MDNS_GROUP); - } - if (socket != null) - { - this.closeMulticastSocket(); - } - socket = new MulticastSocket(DNSConstants.MDNS_PORT); - if ((hostInfo != null) && (localHost.getInterface() != null)) - { - socket.setNetworkInterface(hostInfo.getInterface()); - } - socket.setTimeToLive(255); - socket.joinGroup(group); - } - - private void closeMulticastSocket() - { - if (logger.isDebugEnabled()) - logger.debug("closeMulticastSocket()"); - if (socket != null) - { - // close socket - try - { - socket.leaveGroup(group); - socket.close(); - if (incomingListener != null) - { - incomingListener.join(); - } - } - catch (Exception exception) - { - logger.warn("closeMulticastSocket() Close socket exception ", - exception); - } - socket = null; - } - } - - // State machine - /** - * Sets the state and notifies all objects that wait on JmDNS. - */ - synchronized void advanceState() - { - state = state.advance(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on JmDNS. - */ - synchronized void revertState() - { - state = state.revert(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on JmDNS. - */ - synchronized void cancel() - { - state = DNSState.CANCELED; - notifyAll(); - } - - /** - * Returns the current state of this info. - */ - DNSState getState() - { - return state; - } - - - /** - * Return the DNSCache associated with the cache variable - */ - DNSCache getCache() - { - return cache; - } - - /** - * Return the HostName associated with this JmDNS instance. - * Note: May not be the same as what started. The host name is subject to - * negotiation. - * @return Return the HostName associated with this JmDNS instance. - */ - public String getHostName() - { - return localHost.getName(); - } - - public HostInfo getLocalHost() - { - return localHost; - } - - /** - * Return the address of the interface to which this instance of JmDNS is - * bound. - * @return Return the address of the interface to which this instance - * of JmDNS is bound. - * @throws java.io.IOException - */ - public InetAddress getInterface() - throws IOException - { - return socket.getInterface(); - } - - /** - * Get service information. If the information is not cached, the method - * will block until updated information is received. - * <p/> - * Usage note: Do not call this method from the AWT event dispatcher thread. - * You will make the user interface unresponsive. - * - * @param type fully qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - * @return null if the service information cannot be obtained - */ - public ServiceInfo getServiceInfo(String type, String name) - { - return getServiceInfo(type, name, 3 * 1000); - } - - /** - * Get service information. If the information is not cached, the method - * will block for the given timeout until updated information is received. - * <p/> - * Usage note: If you call this method from the AWT event dispatcher thread, - * use a small timeout, or you will make the user interface unresponsive. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - * @param timeout timeout in milliseconds - * @return null if the service information cannot be obtained - */ - public ServiceInfo getServiceInfo(String type, String name, int timeout) - { - ServiceInfo info = new ServiceInfo(type, name); - new ServiceInfoResolver(info).start(); - - try - { - long end = System.currentTimeMillis() + timeout; - long delay; - synchronized (info) - { - while (!info.hasData() && - (delay = end - System.currentTimeMillis()) > 0) - { - info.wait(delay); - } - } - } - catch (InterruptedException e) - { - // empty - } - - return (info.hasData()) ? info : null; - } - - /** - * Request service information. The information about the service is - * requested and the ServiceListener.resolveService method is called as soon - * as it is available. - * <p/> - * Usage note: Do not call this method from the AWT event dispatcher thread. - * You will make the user interface unresponsive. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - */ - public void requestServiceInfo(String type, String name) - { - requestServiceInfo(type, name, 3 * 1000); - } - - /** - * Request service information. The information about the service - * is requested and the ServiceListener.resolveService method is - * called as soon as it is available. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - * @param timeout timeout in milliseconds - */ - public void requestServiceInfo(String type, String name, int timeout) - { - registerServiceType(type); - ServiceInfo info = new ServiceInfo(type, name); - new ServiceInfoResolver(info).start(); - - try - { - long end = System.currentTimeMillis() + timeout; - long delay; - synchronized (info) - { - while (!info.hasData() && - (delay = end - System.currentTimeMillis()) > 0) - { - info.wait(delay); - } - } - } - catch (InterruptedException e) - { - // empty - } - } - - void handleServiceResolved(ServiceInfo info) - { - List<ServiceListener> list = serviceListeners.get(info.type.toLowerCase()); - if (list != null) - { - ServiceEvent event = - new ServiceEvent(this, info.type, info.getName(), info); - // Iterate on a copy in case listeners will modify it - List<ServiceListener> listCopy - = new ArrayList<ServiceListener> (list); - for (ServiceListener serviceListener : listCopy) - serviceListener.serviceResolved(event); - } - } - - /** - * Listen for service types. - * - * @param listener listener for service types - * @throws java.io.IOException - */ - public void addServiceTypeListener(ServiceTypeListener listener) - throws IOException - { - synchronized (this) - { - typeListeners.remove(listener); - typeListeners.add(listener); - } - - // report cached service types - for (String serviceType : serviceTypes.values()) - { - listener.serviceTypeAdded( - new ServiceEvent(this, serviceType, null, null)); - } - - new TypeResolver().start(); - } - - /** - * Remove listener for service types. - * - * @param listener listener for service types - */ - public void removeServiceTypeListener(ServiceTypeListener listener) - { - synchronized (this) - { - typeListeners.remove(listener); - } - } - - /** - * Listen for services of a given type. The type has to be a fully - * qualified type name such as <code>_http._tcp.local.</code>. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code>. - * @param listener listener for service updates - */ - public void addServiceListener(String type, ServiceListener listener) - { - String lotype = type.toLowerCase(); - removeServiceListener(lotype, listener); - List<ServiceListener> list = null; - synchronized (this) - { - list = serviceListeners.get(lotype); - if (list == null) - { - list = Collections.synchronizedList(new LinkedList<ServiceListener>()); - serviceListeners.put(lotype, list); - } - list.add(listener); - } - - // report cached service types - for (Iterator<DNSCache.CacheNode> i = cache.iterator(); i.hasNext();) - { - for (DNSCache.CacheNode n = i.next(); n != null; n = n.next()) - { - DNSRecord rec = (DNSRecord) n.getValue(); - if (rec.type == DNSConstants.TYPE_SRV) - { - if (rec.name.endsWith(type)) - { - listener.serviceAdded( - new ServiceEvent( - this, - type, - toUnqualifiedName(type, rec.name), - null)); - } - } - } - } - new ServiceResolver(type).start(); - } - - /** - * Remove listener for services of a given type. - * - * @param type of listener to be removed - * @param listener listener for service updates - */ - public void removeServiceListener(String type, ServiceListener listener) - { - type = type.toLowerCase(); - List<ServiceListener> list = serviceListeners.get(type); - if (list != null) - { - synchronized (this) - { - list.remove(listener); - if (list.size() == 0) - { - serviceListeners.remove(type); - } - } - } - } - - /** - * Register a service. The service is registered - * for access by other jmdns clients. - * The name of the service may be changed to make it unique. - * @param info of service - * @throws java.io.IOException - */ - public void registerService(ServiceInfo info) throws IOException - { - registerServiceType(info.type); - - // bind the service to this address - info.server = localHost.getName(); - info.addr = localHost.getAddress(); - - synchronized (this) - { - makeServiceNameUnique(info); - services.put(info.getQualifiedName().toLowerCase(), info); - } - - new /*Service*/Prober().start(); - try - { - synchronized (info) - { - while (info.getState().compareTo(DNSState.ANNOUNCED) < 0) - { - info.wait(); - } - } - } - catch (InterruptedException e) - { - logger.error(e.getMessage(), e); - } - if (logger.isDebugEnabled()) - logger.debug("registerService() JmDNS registered service as " + info); - } - - /** - * Unregister a service. The service should have been registered. - * @param info of service - */ - public void unregisterService(ServiceInfo info) - { - synchronized (this) - { - services.remove(info.getQualifiedName().toLowerCase()); - } - info.cancel(); - - // Note: We use this lock object to synchronize on it. - // Synchronizing on another object (e.g. the ServiceInfo) does - // not make sense, because the sole purpose of the lock is to - // wait until the canceler has finished. If we synchronized on - // the ServiceInfo or on the Canceler, we would block all - // accesses to synchronized methods on that object. This is not - // what we want! - Object lock = new Object(); - new Canceler(info, lock).start(); - - // Remind: We get a deadlock here, if the Canceler does not run! - try - { - synchronized (lock) - { - lock.wait(); - } - } - catch (InterruptedException e) - { - // empty - } - } - - /** - * Unregister all services. - */ - public void unregisterAllServices() - { - if (logger.isDebugEnabled()) - logger.debug("unregisterAllServices()"); - if (services.size() == 0) - { - return; - } - - Collection<ServiceInfo> list; - synchronized (this) - { - list = new LinkedList<ServiceInfo>(services.values()); - services.clear(); - } - for (Iterator<ServiceInfo> iterator = list.iterator(); iterator.hasNext();) - { - iterator.next().cancel(); - } - - - Object lock = new Object(); - new Canceler(list, lock).start(); - // Remind: We get a livelock here, if the Canceler does not run! - try - { - synchronized (lock) - { - if (!closed) - { - lock.wait(); - } - } - } - catch (InterruptedException e) - { - // empty - } - } - - /** - * Register a service type. If this service type was not already known, - * all service listeners will be notified of the new service type. - * Service types are automatically registered as they are discovered. - * @param type of service - */ - public void registerServiceType(String type) - { - String name = type.toLowerCase(); - if (serviceTypes.get(name) == null) - { - if ((type.indexOf("._mdns._udp.") < 0) && - !type.endsWith(".in-addr.arpa.")) - { - Collection<ServiceTypeListener> list; - synchronized (this) - { - serviceTypes.put(name, type); - list = new LinkedList<ServiceTypeListener>(typeListeners); - } - for (ServiceTypeListener listener : list) - listener - .serviceTypeAdded( - new ServiceEvent(this, type, null, null)); - } - } - } - - /** - * Generate a possibly unique name for a service using the information we - * have in the cache. - * - * @return returns true, if the name of the service info had to be changed. - */ - private boolean makeServiceNameUnique(ServiceInfo info) - { - String originalQualifiedName = info.getQualifiedName(); - long now = System.currentTimeMillis(); - - boolean collision; - do - { - collision = false; - - // Check for collision in cache - for (DNSCache.CacheNode j = cache.find( - info.getQualifiedName().toLowerCase()); - j != null; - j = j.next()) - { - DNSRecord a = (DNSRecord) j.getValue(); - if ((a.type == DNSConstants.TYPE_SRV) && !a.isExpired(now)) - { - DNSRecord.Service s = (DNSRecord.Service) a; - if (s.port != info.port || !s.server.equals(localHost.getName())) - { - if (logger.isDebugEnabled()) - logger.debug("makeServiceNameUnique() " + - "JmDNS.makeServiceNameUnique srv collision:" + - a + " s.server=" + s.server + " " + - localHost.getName() + " equals:" + - (s.server.equals(localHost.getName()))); - info.setName(incrementName(info.getName())); - collision = true; - break; - } - } - } - - // Check for collision with other service infos published by JmDNS - Object selfService = - services.get(info.getQualifiedName().toLowerCase()); - if (selfService != null && selfService != info) - { - info.setName(incrementName(info.getName())); - collision = true; - } - } - while (collision); - - return !(originalQualifiedName.equals(info.getQualifiedName())); - } - - String incrementName(String name) - { - try - { - int l = name.lastIndexOf('('); - int r = name.lastIndexOf(')'); - if ((l >= 0) && (l < r)) - { - name = name.substring(0, l) + "(" + - (Integer.parseInt(name.substring(l + 1, r)) + 1) + ")"; - } - else - { - name += " (2)"; - } - } - catch (NumberFormatException e) - { - name += " (2)"; - } - return name; - } - - /** - * Add a listener for a question. The listener will receive updates - * of answers to the question as they arrive, or from the cache if they - * are already available. - * @param listener to be added - * @param question - which the listener is responsible for. - */ - public void addListener(DNSListener listener, DNSQuestion question) - { - long now = System.currentTimeMillis(); - - // add the new listener - synchronized (this) - { - listeners.add(listener); - } - - // report existing matched records - if (question != null) - { - for (DNSCache.CacheNode i = cache.find(question.name); - i != null; - i = i.next()) - { - DNSRecord c = (DNSRecord) i.getValue(); - if (question.answeredBy(c) && !c.isExpired(now)) - { - listener.updateRecord(this, now, c); - } - } - } - } - - /** - * Remove a listener from all outstanding questions. - * The listener will no longer receive any updates. - */ - void removeListener(DNSListener listener) - { - synchronized (this) - { - listeners.remove(listener); - } - } - - - // Remind: Method updateRecord should receive a better name. - /** - * Notify all listeners that a record was updated. - */ - void updateRecord(long now, DNSRecord rec) - { - // We do not want to block the entire DNS - // while we are updating the record for each listener (service info) - List<DNSListener> listenerList = null; - synchronized (this) - { - listenerList = new ArrayList<DNSListener>(listeners); - } - - //System.out.println("OUT OF MUTEX!!!!!"); - - for (DNSListener listener : listenerList) - listener.updateRecord(this, now, rec); - - if (rec.type == DNSConstants.TYPE_PTR || - rec.type == DNSConstants.TYPE_SRV) - { - List<ServiceListener> serviceListenerList = null; - synchronized (this) - { - serviceListenerList = serviceListeners.get(rec.name.toLowerCase()); - // Iterate on a copy in case listeners will modify it - if (serviceListenerList != null) - { - serviceListenerList = new ArrayList<ServiceListener>(serviceListenerList); - } - } - if (serviceListenerList != null) - { - boolean expired = rec.isExpired(now); - String type = rec.getName(); - String name = ((DNSRecord.Pointer) rec).getAlias(); - // DNSRecord old = (DNSRecord)services.get(name.toLowerCase()); - if (!expired) - { - // new record - ServiceEvent event = - new ServiceEvent( - this, - type, - toUnqualifiedName(type, name), - null); - for (Iterator<ServiceListener> iterator = serviceListenerList.iterator(); - iterator.hasNext();) - { - iterator.next().serviceAdded(event); - } - } - else - { - // expire record - ServiceEvent event = - new ServiceEvent( - this, - type, - toUnqualifiedName(type, name), - null); - for (Iterator<ServiceListener> iterator = serviceListenerList.iterator(); - iterator.hasNext();) - { - iterator.next().serviceRemoved(event); - } - } - } - } - } - - /** - * Handle an incoming response. Cache answers, and pass them on to - * the appropriate questions. - */ - private void handleResponse(DNSIncoming msg) - throws IOException - { - long now = System.currentTimeMillis(); - - boolean hostConflictDetected = false; - boolean serviceConflictDetected = false; - - if (logger.isTraceEnabled()) - logger.trace("JMDNS/handleResponse received " + - msg.answers.size()+ " messages"); - for (DNSRecord rec : msg.answers) - { - if (logger.isTraceEnabled()) - logger.trace("PRINT: "+ rec); - //cache.add(rec); - } - - for (DNSRecord rec : msg.answers) - { - boolean isInformative = false; - boolean expired = rec.isExpired(now); - - if (logger.isTraceEnabled()) - logger.trace("JMDNS received : " + rec + " expired: "+expired); - - // update the cache - DNSRecord c = (DNSRecord) cache.get(rec); - if (c != null) - { - if (logger.isTraceEnabled()) - logger.trace("JMDNS has found "+rec+" in cache"); - if (expired) - { - isInformative = true; - cache.remove(c); - } - else - { - /* Special case for SIP Communicator. - * We want to be informed if a cache entry is modified - */ -// if ((c.isUnique() -// && c.getType() == DNSConstants.TYPE_TXT -// && ((c.getClazz() & DNSConstants.CLASS_IN) != 0))) -// isInformative = true; -// c.resetTTL(rec); -// rec = c; - if (logger.isTraceEnabled()) - logger.trace( - new Boolean(c.isUnique()).toString() + - c.getType()+c.getClazz() + "/" + - DNSConstants.TYPE_TXT + " "+DNSConstants.CLASS_IN); - - if ((rec.isUnique() - && ((rec.getType() & DNSConstants.TYPE_TXT) != 0) - && ((rec.getClazz() & DNSConstants.CLASS_IN) != 0))) - { - if (logger.isTraceEnabled()) - logger.trace("UPDATING CACHE !! "); - isInformative = true; - cache.remove(c); - cache.add(rec); - } - else - { - c.resetTTL(rec); - rec = c; - } - } - } - else - { - if (!expired) - { - isInformative = true; - if (logger.isTraceEnabled()) - logger.trace("Adding "+rec+" to the cache"); - cache.add(rec); - } - } - switch (rec.type) - { - case DNSConstants.TYPE_PTR: - // handle _mdns._udp records - if (rec.getName().indexOf("._mdns._udp.") >= 0) - { - if (!expired && - rec.name.startsWith("_services._mdns._udp.")) - { - isInformative = true; - registerServiceType(((DNSRecord.Pointer)rec).alias); - } - continue; - } - registerServiceType(rec.name); - break; - } - - - if ((rec.getType() == DNSConstants.TYPE_A) || - (rec.getType() == DNSConstants.TYPE_AAAA)) - { - hostConflictDetected |= rec.handleResponse(this); - } - else - { - serviceConflictDetected |= rec.handleResponse(this); - } - - // notify the listeners - if (isInformative) - { - updateRecord(now, rec); - } - - - } - - if (hostConflictDetected || serviceConflictDetected) - { - new Prober().start(); - } - } - - /** - * Handle an incoming query. See if we can answer any part of it - * given our service infos. - */ - private void handleQuery(DNSIncoming in, InetAddress addr, int port) - throws IOException - { - // Track known answers - boolean hostConflictDetected = false; - boolean serviceConflictDetected = false; - long expirationTime = System.currentTimeMillis() + - DNSConstants.KNOWN_ANSWER_TTL; - for (DNSRecord answer : in.answers) - { - if ((answer.getType() == DNSConstants.TYPE_A) || - (answer.getType() == DNSConstants.TYPE_AAAA)) - { - hostConflictDetected |= - answer.handleQuery(this, expirationTime); - } - else - { - serviceConflictDetected |= - answer.handleQuery(this, expirationTime); - } - } - - if (plannedAnswer != null) - { - plannedAnswer.append(in); - } - else - { - if (in.isTruncated()) - { - plannedAnswer = in; - } - - new Responder(in, addr, port).start(); - } - - if (hostConflictDetected || serviceConflictDetected) - { - new Prober().start(); - } - } - - /** - * Add an answer to a question. Deal with the case when the - * outgoing packet overflows - */ - DNSOutgoing addAnswer(DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out, - DNSRecord rec) - throws IOException - { - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - } - try - { - out.addAnswer(in, rec); - } - catch (IOException e) - { - out.flags |= DNSConstants.FLAGS_TC; - out.id = in.id; - out.finish(); - send(out); - - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - out.addAnswer(in, rec); - } - return out; - } - - - /** - * Send an outgoing multicast DNS message. - */ - private void send(DNSOutgoing out) throws IOException - { - out.finish(); - if (!out.isEmpty()) - { - DatagramPacket packet = - new DatagramPacket( - out.data, out.off, group, DNSConstants.MDNS_PORT); - - try - { - DNSIncoming msg = new DNSIncoming(packet); - if (logger.isTraceEnabled()) - logger.trace("send() JmDNS out:" + msg.print(true)); - } - catch (IOException exc) - { - logger.error( - "send(DNSOutgoing) - JmDNS can not parse what it sends!!!", - exc); - } - socket.send(packet); - } - } - - /** - * Listen for multicast packets. - */ - class SocketListener implements Runnable - { - public void run() - { - try - { - byte buf[] = new byte[DNSConstants.MAX_MSG_ABSOLUTE]; - DatagramPacket packet = new DatagramPacket(buf, buf.length); - while (state != DNSState.CANCELED) - { - packet.setLength(buf.length); - socket.receive(packet); - if (state == DNSState.CANCELED) - { - break; - } - try - { - if (localHost.shouldIgnorePacket(packet)) - { - continue; - } - - DNSIncoming msg = new DNSIncoming(packet); - if (logger.isTraceEnabled()) - logger.trace("SocketListener.run() JmDNS in:" + - msg.print(true)); - - synchronized (ioLock) - { - if (msg.isQuery()) - { - if (packet.getPort() != DNSConstants.MDNS_PORT) - { - handleQuery(msg, - packet.getAddress(), - packet.getPort()); - } - handleQuery(msg, group, DNSConstants.MDNS_PORT); - } - else - { - handleResponse(msg); - } - } - } - catch (IOException e) - { - logger.warn( "run() exception ", e); - } - } - } - catch (IOException e) - { - if (state != DNSState.CANCELED) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - } - - - /** - * Periodicaly removes expired entries from the cache. - */ - private class RecordReaper extends TimerTask - { - public void start() - { - timer.schedule( this, - DNSConstants.RECORD_REAPER_INTERVAL, - DNSConstants.RECORD_REAPER_INTERVAL); - } - - @Override - public void run() - { - synchronized (JmDNS.this) - { - if (state == DNSState.CANCELED) - { - return; - } - if (logger.isTraceEnabled()) - logger.trace("run() JmDNS reaping cache"); - - // Remove expired answers from the cache - // ------------------------------------- - // To prevent race conditions, we defensively copy all cache - // entries into a list. - List<DNSEntry> list = new ArrayList<DNSEntry>(); - synchronized (cache) - { - for (Iterator<DNSCache.CacheNode> i = cache.iterator(); - i.hasNext();) - { - for (DNSCache.CacheNode n = i.next(); - n != null; - n = n.next()) - { - list.add(n.getValue()); - } - } - } - // Now, we remove them. - long now = System.currentTimeMillis(); - for (Iterator<DNSEntry> i = list.iterator(); i.hasNext();) - { - DNSRecord c = (DNSRecord)i.next(); - if (c.isExpired(now)) - { - updateRecord(now, c); - cache.remove(c); - } - } - } - } - } - - - /** - * The Prober sends three consecutive probes for all service infos - * that needs probing as well as for the host name. - * The state of each service info of the host name is advanced, - * when a probe has been sent for it. - * When the prober has run three times, it launches an Announcer. - * <p/> - * If a conflict during probes occurs, the affected service - * infos (and affected host name) are taken away from the prober. - * This eventually causes the prober tho cancel itself. - */ - private class Prober extends TimerTask - { - /** - * The state of the prober. - */ - DNSState taskState = DNSState.PROBING_1; - - public Prober() - { - // Associate the host name to this, if it needs probing - if (state == DNSState.PROBING_1) - { - task = this; - } - // Associate services to this, if they need probing - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> iterator = services.values().iterator(); - iterator.hasNext();) - { - ServiceInfo info = iterator.next(); - if (info.getState() == DNSState.PROBING_1) - { - info.task = this; - } - } - } - } - - - public void start() - { - long now = System.currentTimeMillis(); - if (now - lastThrottleIncrement < - DNSConstants.PROBE_THROTTLE_COUNT_INTERVAL) - { - throttle++; - } - else - { - throttle = 1; - } - lastThrottleIncrement = now; - - if (state == DNSState.ANNOUNCED && - throttle < DNSConstants.PROBE_THROTTLE_COUNT) - { - timer.schedule(this, - random.nextInt(1 + DNSConstants.PROBE_WAIT_INTERVAL), - DNSConstants.PROBE_WAIT_INTERVAL); - } - else - { - timer.schedule(this, - DNSConstants.PROBE_CONFLICT_INTERVAL, - DNSConstants.PROBE_CONFLICT_INTERVAL); - } - } - - @Override - public boolean cancel() - { - // Remove association from host name to this - if (task == this) - { - task = null; - } - - // Remove associations from services to this - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> i = services.values().iterator(); - i.hasNext();) - { - ServiceInfo info = i.next(); - if (info.task == this) - { - info.task = null; - } - } - } - - return super.cancel(); - } - - @Override - public void run() - { - synchronized (ioLock) - { - DNSOutgoing out = null; - try - { - // send probes for JmDNS itself - if (state == taskState && task == this) - { - if (out == null) - { - out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - } - out.addQuestion( - new DNSQuestion( - localHost.getName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAuthorativeAnswer(answer); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAuthorativeAnswer(answer); - } - advanceState(); - } - // send probes for services - // Defensively copy the services into a local list, - // to prevent race conditions with methods registerService - // and unregisterService. - List<ServiceInfo> list; - synchronized (JmDNS.this) - { - list = new LinkedList<ServiceInfo>(services.values()); - } - for (Iterator<ServiceInfo> i = list.iterator(); i.hasNext();) - { - ServiceInfo info = i.next(); - - synchronized (info) - { - if (info.getState() == taskState && - info.task == this) - { - info.advanceState(); - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS probing " + - info.getQualifiedName() + " state " + - info.getState()); - - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - } - out.addAuthorativeAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName())); - } - } - } - if (out != null) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS probing #" + taskState); - send(out); - } - else - { - // If we have nothing to send, another timer taskState - // ahead of us has done the job for us. We can cancel. - cancel(); - return; - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - - taskState = taskState.advance(); - if (!taskState.isProbing()) - { - cancel(); - - new Announcer().start(); - } - } - } - - } - - /** - * The Announcer sends an accumulated query of all announces, and advances - * the state of all serviceInfos, for which it has sent an announce. - * The Announcer also sends announcements and advances the state of JmDNS - * itself. - * <p/> - * When the announcer has run two times, it finishes. - */ - private class Announcer extends TimerTask - { - /** - * The state of the announcer. - */ - DNSState taskState = DNSState.ANNOUNCING_1; - - public Announcer() - { - // Associate host to this, if it needs announcing - if (state == DNSState.ANNOUNCING_1) - { - task = this; - } - // Associate services to this, if they need announcing - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> s = services.values().iterator(); s.hasNext();) - { - ServiceInfo info = s.next(); - if (info.getState() == DNSState.ANNOUNCING_1) - { - info.task = this; - } - } - } - } - - public void start() - { - timer.schedule(this, - DNSConstants.ANNOUNCE_WAIT_INTERVAL, - DNSConstants.ANNOUNCE_WAIT_INTERVAL); - } - - @Override - public boolean cancel() - { - // Remove association from host to this - if (task == this) - { - task = null; - } - - // Remove associations from services to this - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> i = services.values().iterator(); - i.hasNext();) - { - ServiceInfo info = i.next(); - if (info.task == this) - { - info.task = null; - } - } - } - - return super.cancel(); - } - - @Override - public void run() - { - DNSOutgoing out = null; - try - { - // send probes for JmDNS itself - if (state == taskState) - { - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - } - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - advanceState(); - } - // send announces for services - // Defensively copy the services into a local list, - // to prevent race conditions with methods registerService - // and unregisterService. - List<ServiceInfo> list; - synchronized (JmDNS.this) - { - list = new ArrayList<ServiceInfo>(services.values()); - } - for (Iterator<ServiceInfo> i = list.iterator(); i.hasNext();) - { - ServiceInfo info = i.next(); - synchronized (info) - { - if (info.getState() == taskState && info.task == this) - { - info.advanceState(); - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announcing " + - info.getQualifiedName() + - " state " + info.getState()); - - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | - DNSConstants.FLAGS_AA); - } - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName()), 0); - out.addAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName()), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.text), 0); - } - } - } - if (out != null) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announcing #" + taskState); - send(out); - } - else - { - // If we have nothing to send, another timer taskState ahead - // of us has done the job for us. We can cancel. - cancel(); - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - - taskState = taskState.advance(); - if (!taskState.isAnnouncing()) - { - cancel(); - - new Renewer().start(); - } - } - } - - /** - * The Renewer is there to send renewal announcment - * when the record expire for ours infos. - */ - private class Renewer extends TimerTask - { - /** - * The state of the announcer. - */ - DNSState taskState = DNSState.ANNOUNCED; - - public Renewer() - { - // Associate host to this, if it needs renewal - if (state == DNSState.ANNOUNCED) - { - task = this; - } - // Associate services to this, if they need renewal - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> s = services.values().iterator(); s.hasNext();) - { - ServiceInfo info = s.next(); - if (info.getState() == DNSState.ANNOUNCED) - { - info.task = this; - } - } - } - } - - public void start() - { - timer.schedule(this, - DNSConstants.ANNOUNCED_RENEWAL_TTL_INTERVAL, - DNSConstants.ANNOUNCED_RENEWAL_TTL_INTERVAL); - } - - @Override - public boolean cancel() - { - // Remove association from host to this - if (task == this) - { - task = null; - } - - // Remove associations from services to this - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> i = services.values().iterator(); - i.hasNext();) - { - ServiceInfo info = i.next(); - if (info.task == this) - { - info.task = null; - } - } - } - - return super.cancel(); - } - - @Override - public void run() - { - DNSOutgoing out = null; - try - { - // send probes for JmDNS itself - if (state == taskState) - { - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - } - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - advanceState(); - } - // send announces for services - // Defensively copy the services into a local list, - // to prevent race conditions with methods registerService - // and unregisterService. - List<ServiceInfo> list; - synchronized (JmDNS.this) - { - list = new ArrayList<ServiceInfo>(services.values()); - } - for (Iterator<ServiceInfo> i = list.iterator(); i.hasNext();) - { - ServiceInfo info = i.next(); - synchronized (info) - { - if (info.getState() == taskState && info.task == this) - { - info.advanceState(); - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announced " + - info.getQualifiedName() + " state " + info.getState()); - - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | - DNSConstants.FLAGS_AA); - } - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName()), 0); - out.addAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName()), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.text), 0); - } - } - } - if (out != null) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announced"); - send(out); - } - else - { - // If we have nothing to send, another timer taskState ahead - // of us has done the job for us. We can cancel. - cancel(); - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - - taskState = taskState.advance(); - if (!taskState.isAnnounced()) - { - cancel(); - - } - } - } - - /** - * The Responder sends a single answer for the specified service infos - * and for the host name. - */ - private class Responder extends TimerTask - { - private DNSIncoming in; - private InetAddress addr; - private int port; - - public Responder(DNSIncoming in, InetAddress addr, int port) - { - this.in = in; - this.addr = addr; - this.port = port; - } - - public void start() - { - // According to draft-cheshire-dnsext-multicastdns.txt - // chapter "8 Responding": - // We respond immediately if we know for sure, that we are - // the only one who can respond to the query. - // In all other cases, we respond within 20-120 ms. - // - // According to draft-cheshire-dnsext-multicastdns.txt - // chapter "7.2 Multi-Packet Known Answer Suppression": - // We respond after 20-120 ms if the query is truncated. - - boolean iAmTheOnlyOne = true; - for (DNSEntry entry : in.questions) - { - if (entry instanceof DNSQuestion) - { - DNSQuestion q = (DNSQuestion) entry; - if (logger.isTraceEnabled()) - logger.trace("start() question=" + q); - iAmTheOnlyOne &= (q.type == DNSConstants.TYPE_SRV - || q.type == DNSConstants.TYPE_TXT - || q.type == DNSConstants.TYPE_A - || q.type == DNSConstants.TYPE_AAAA - || localHost.getName().equalsIgnoreCase(q.name) - || services.containsKey(q.name.toLowerCase())); - if (!iAmTheOnlyOne) - { - break; - } - } - } - int delay = (iAmTheOnlyOne && !in.isTruncated()) ? - 0 : - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + - random.nextInt( - DNSConstants.RESPONSE_MAX_WAIT_INTERVAL - - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + 1) - - in.elapseSinceArrival(); - if (delay < 0) - { - delay = 0; - } - if (logger.isTraceEnabled()) - logger.trace("start() Responder chosen delay=" + delay); - timer.schedule(this, delay); - } - - @Override - public void run() - { - synchronized (ioLock) - { - if (plannedAnswer == in) - { - plannedAnswer = null; - } - - // We use these sets to prevent duplicate records - // FIXME - This should be moved into DNSOutgoing - HashSet<DNSQuestion> questions = new HashSet<DNSQuestion>(); - HashSet<DNSRecord> answers = new HashSet<DNSRecord>(); - - - if (state == DNSState.ANNOUNCED) - { - try - { - boolean isUnicast = (port != DNSConstants.MDNS_PORT); - - - // Answer questions - for (Iterator<DNSEntry> iterator = in.questions.iterator(); - iterator.hasNext();) - { - DNSEntry entry = iterator.next(); - if (entry instanceof DNSQuestion) - { - DNSQuestion q = (DNSQuestion) entry; - - // for unicast responses the question - // must be included - if (isUnicast) - { - //out.addQuestion(q); - questions.add(q); - } - - int type = q.type; - if (type == DNSConstants.TYPE_ANY || - type == DNSConstants.TYPE_SRV) - { // I ama not sure of why there is a special - // case here [PJYF Oct 15 2004] - if (localHost.getName(). - equalsIgnoreCase(q.getName())) - { - // type = DNSConstants.TYPE_A; - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - type = DNSConstants.TYPE_IGNORE; - } - else - { - if (serviceTypes.containsKey( - q.getName().toLowerCase())) - { - type = DNSConstants.TYPE_PTR; - } - } - } - - switch (type) - { - case DNSConstants.TYPE_A: - { - // Answer a query for a domain name - //out = addAnswer( in, addr, port, out, host ); - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - break; - } - case DNSConstants.TYPE_AAAA: - { - // Answer a query for a domain name - DNSRecord answer = - localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - break; - } - case DNSConstants.TYPE_PTR: - { - // Answer a query for services of a given type - - // find matching services - for (Iterator<ServiceInfo> serviceIterator = - services.values().iterator(); - serviceIterator.hasNext();) - { - ServiceInfo info = serviceIterator.next(); - if (info.getState() == DNSState.ANNOUNCED) - { - if (q.name.equalsIgnoreCase(info.type)) - { - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answer = - localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answers.add( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName())); - answers.add( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName())); - answers.add( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text)); - } - } - } - if (q.name.equalsIgnoreCase("_services._mdns._udp.local.")) - { - for (Iterator<String> serviceTypeIterator = serviceTypes.values().iterator(); - serviceTypeIterator.hasNext();) - { - answers.add( - new DNSRecord.Pointer( - "_services._mdns._udp.local.", - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - serviceTypeIterator.next())); - } - } - break; - } - case DNSConstants.TYPE_SRV: - case DNSConstants.TYPE_ANY: - case DNSConstants.TYPE_TXT: - { - ServiceInfo info = services.get(q.name.toLowerCase()); - if (info != null && - info.getState() == DNSState.ANNOUNCED) - { - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answer = - localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answers.add( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName())); - answers.add( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName())); - answers.add( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text)); - } - break; - } - default : - { - //System.out.println("JmDNSResponder.unhandled query:"+q); - break; - } - } - } - } - - - // remove known answers, if the ttl is at least half of - // the correct value. (See Draft Cheshire chapter 7.1.). - for (DNSRecord knownAnswer : in.answers) - { - if (knownAnswer.ttl > DNSConstants.DNS_TTL / 2 && - answers.remove(knownAnswer)) - { - if (logger.isDebugEnabled()) - logger.debug( - "JmDNS Responder Known Answer Removed"); - } - } - - - // responde if we have answers - if (answers.size() != 0) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS responding"); - DNSOutgoing out = null; - if (isUnicast) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE - | DNSConstants.FLAGS_AA, - false); - } - - for (Iterator<DNSQuestion> i = questions.iterator(); - i.hasNext();) - { - out.addQuestion(i.next()); - } - for (Iterator<DNSRecord> i = answers.iterator(); - i.hasNext();) - { - out = addAnswer(in, addr, port, out, i.next()); - } - send(out); - } - this.cancel(); - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - close(); - } - } - } - } - } - - /** - * Helper class to resolve service types. - * <p/> - * The TypeResolver queries three times consecutively for service types, and then - * removes itself from the timer. - * <p/> - * The TypeResolver will run only if JmDNS is in state ANNOUNCED. - */ - private class TypeResolver extends TimerTask - { - public void start() - { - timer.schedule(this, - DNSConstants.QUERY_WAIT_INTERVAL, - DNSConstants.QUERY_WAIT_INTERVAL); - } - - /** - * Counts the number of queries that were sent. - */ - int count = 0; - - @Override - public void run() - { - try - { - if (state == DNSState.ANNOUNCED) - { - if (count++ < 3) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS querying type"); - DNSOutgoing out = - new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - "_services._mdns._udp.local.", - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN)); - for (String serviceType : serviceTypes.values()) - { - out.addAnswer( - new DNSRecord.Pointer( - "_services._mdns._udp.local.", - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - serviceType), 0); - } - send(out); - } - else - { - // After three queries, we can quit. - this.cancel(); - } - } - else - { - if (state == DNSState.CANCELED) - { - this.cancel(); - } - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * The ServiceResolver queries three times consecutively for services of - * a given type, and then removes itself from the timer. - * <p/> - * The ServiceResolver will run only if JmDNS is in state ANNOUNCED. - * REMIND: Prevent having multiple service resolvers for the same type in the - * timer queue. - */ - private class ServiceResolver extends TimerTask - { - /** - * Counts the number of queries being sent. - */ - int count = 0; - private String type; - - public ServiceResolver(String type) - { - this.type = type; - } - - public void start() - { - timer.schedule(this, - DNSConstants.QUERY_WAIT_INTERVAL, - DNSConstants.QUERY_WAIT_INTERVAL); - } - - @Override - public void run() - { - try - { - if (state == DNSState.ANNOUNCED) - { - if (count++ < 3) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS querying service"); - long now = System.currentTimeMillis(); - DNSOutgoing out = - new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN)); - for (Iterator<ServiceInfo> s = services.values().iterator(); s.hasNext();) - { - final ServiceInfo info = s.next(); - try - { - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName()), now); - } - catch (IOException ee) - { - break; - } - } - send(out); - } - else - { - // After three queries, we can quit. - this.cancel(); - } - } - else - { - if (state == DNSState.CANCELED) - { - this.cancel(); - } - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * The ServiceInfoResolver queries up to three times consecutively for - * a service info, and then removes itself from the timer. - * <p/> - * The ServiceInfoResolver will run only if JmDNS is in state ANNOUNCED. - * REMIND: Prevent having multiple service resolvers for the same info in the - * timer queue. - */ - private class ServiceInfoResolver extends TimerTask - { - /** - * Counts the number of queries being sent. - */ - int count = 0; - private ServiceInfo info; - - public ServiceInfoResolver(ServiceInfo info) - { - this.info = info; - info.dns = JmDNS.this; - addListener(info, - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - } - - public void start() - { - timer.schedule(this, - DNSConstants.QUERY_WAIT_INTERVAL, - DNSConstants.QUERY_WAIT_INTERVAL); - } - - @Override - public void run() - { - try - { - if (state == DNSState.ANNOUNCED) - { - if (count++ < 3 && !info.hasData()) - { - long now = System.currentTimeMillis(); - DNSOutgoing out = - new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN)); - out.addQuestion( - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN)); - if (info.server != null) - { - out.addQuestion( - new DNSQuestion( - info.server, - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN)); - } - out.addAnswer((DNSRecord) cache.get( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN), now); - out.addAnswer((DNSRecord) cache.get( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN), now); - if (info.server != null) - { - out.addAnswer((DNSRecord) cache.get( - info.server, - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN), now); - } - send(out); - } - else - { - // After three queries, we can quit. - this.cancel(); - removeListener(info); - } - } - else - { - if (state == DNSState.CANCELED) - { - this.cancel(); - removeListener(info); - } - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * The Canceler sends two announces with TTL=0 for the specified services. - */ - /* TODO: Clarify whether 2 or 3 announces should be sent. The header says 2, - * run() uses the (misleading) ++count < 3 (while all other tasks use count++ < 3) - * and the comment in the else block in run() says: "After three successful..." - */ - public class Canceler extends TimerTask - { - /** - * Counts the number of announces being sent. - */ - int count = 0; - /** - * The services that need cancelling. - * Note: We have to use a local variable here, because the services - * that are canceled, are removed immediately from variable JmDNS.services. - */ - private ServiceInfo[] infos; - /** - * We call notifyAll() on the lock object, when we have canceled the - * service infos. - * This is used by method JmDNS.unregisterService() and - * JmDNS.unregisterAllServices, to ensure that the JmDNS - * socket stays open until the Canceler has canceled all services. - * <p/> - * Note: We need this lock, because ServiceInfos do the transition from - * state ANNOUNCED to state CANCELED before we get here. We could get - * rid of this lock, if we added a state named CANCELLING to DNSState. - */ - private Object lock; - int ttl = 0; - - public Canceler(ServiceInfo info, Object lock) - { - this.infos = new ServiceInfo[]{info}; - this.lock = lock; - addListener(info, - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - } - - public Canceler(ServiceInfo[] infos, Object lock) - { - this.infos = infos; - this.lock = lock; - } - - public Canceler(Collection<ServiceInfo> infos, Object lock) - { - this.infos = infos.toArray(new ServiceInfo[infos.size()]); - this.lock = lock; - } - - public void start() - { - timer.schedule(this, 0, DNSConstants.ANNOUNCE_WAIT_INTERVAL); - } - - @Override - public void run() - { - try - { - if (++count < 3) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS canceling service"); - // announce the service - //long now = System.currentTimeMillis(); - DNSOutgoing out = - new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - for (int i = 0; i < infos.length; i++) - { - ServiceInfo info = infos[i]; - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - ttl, - info.getQualifiedName()), 0); - out.addAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - ttl, - info.priority, - info.weight, - info.port, - localHost.getName()), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN, - ttl, - info.text), 0); - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - } - send(out); - } - else - { - // After three successful announcements, we are finished. - synchronized (lock) - { - closed=true; - lock.notifyAll(); - } - this.cancel(); - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * Recover jmdns when there is an error. - */ - protected void recover() - { - if (logger.isDebugEnabled()) - logger.debug("recover()"); - // We have an IO error so lets try to recover if anything happens lets close it. - // This should cover the case of the IP address changing under our feet - if (DNSState.CANCELED != state) - { - synchronized (this) - { // Synchronize only if we are not already in process to prevent dead locks - // - if (logger.isDebugEnabled()) - logger.debug("recover() Cleanning up"); - // Stop JmDNS - state = DNSState.CANCELED; // This protects against recursive calls - - // We need to keep a copy for reregistration - Collection<ServiceInfo> oldServiceInfos = new ArrayList<ServiceInfo>(services.values()); - - // Cancel all services - unregisterAllServices(); - disposeServiceCollectors(); - // - // close multicast socket - closeMulticastSocket(); - // - cache.clear(); - if (logger.isDebugEnabled()) - logger.debug("recover() All is clean"); - // - // All is clear now start the services - // - try - { - openMulticastSocket(localHost); - start(oldServiceInfos); - } - catch (Exception exception) - { - logger.warn( - "recover() Start services exception ", exception); - } - logger.warn( "recover() We are back!"); - } - } - } - - /** - * Close down jmdns. Release all resources and unregister all services. - */ - public void close() - { - if (state != DNSState.CANCELED) - { - synchronized (this) - { // Synchronize only if we are not already in process to prevent dead locks - // Stop JmDNS - state = DNSState.CANCELED; // This protects against recursive calls - - unregisterAllServices(); - disposeServiceCollectors(); - - // close socket - closeMulticastSocket(); - - // Stop the timer - timer.cancel(); - } - } - } - - /** - * List cache entries, for debugging only. - */ - void print() - { - if (logger.isInfoEnabled()) - logger.info("---- cache ----\n"); - cache.print(); - if (logger.isInfoEnabled()) - logger.info("\n"); - } - - /** - * List Services and serviceTypes. - * Debugging Only - */ - - public void printServices() - { - if (logger.isInfoEnabled()) - logger.info(toString()); - } - - @Override - public String toString() - { - StringBuffer aLog = new StringBuffer(); - aLog.append("\t---- Services -----"); - if (services != null) - { - for (Map.Entry<String, ServiceInfo> entry : services.entrySet()) - { - aLog.append("\n\t\tService: " + entry.getKey() + ": " - + entry.getValue()); - } - } - aLog.append("\n"); - aLog.append("\t---- Types ----"); - if (serviceTypes != null) - { - for (Map.Entry<String, String> entry : serviceTypes.entrySet()) - { - aLog.append("\n\t\tType: " + entry.getKey() + ": " - + entry.getValue()); - } - } - aLog.append("\n"); - aLog.append(cache.toString()); - aLog.append("\n"); - aLog.append("\t---- Service Collectors ----"); - if (serviceCollectors != null) - { - synchronized (serviceCollectors) - { - for (Map.Entry<String, ServiceCollector> entry - : serviceCollectors.entrySet()) - { - aLog.append("\n\t\tService Collector: " + entry.getKey() - + ": " + entry.getValue()); - } - serviceCollectors.clear(); - } - } - return aLog.toString(); - } - - /** - * Returns a list of service infos of the specified type. - * - * @param type Service type name, such as <code>_http._tcp.local.</code>. - * @return An array of service instance names. - */ - public ServiceInfo[] list(String type) - { - // Implementation note: The first time a list for a given type is - // requested, a ServiceCollector is created which collects service - // infos. This greatly speeds up the performance of subsequent calls - // to this method. The caveats are, that 1) the first call to this method - // for a given type is slow, and 2) we spawn a ServiceCollector - // instance for each service type which increases network traffic a - // little. - - ServiceCollector collector; - - boolean newCollectorCreated; - synchronized (serviceCollectors) - { - collector = serviceCollectors.get(type); - if (collector == null) - { - collector = new ServiceCollector(type); - serviceCollectors.put(type, collector); - addServiceListener(type, collector); - newCollectorCreated = true; - } - else - { - newCollectorCreated = false; - } - } - - // After creating a new ServiceCollector, we collect service infos for - // 200 milliseconds. This should be enough time, to get some service - // infos from the network. - if (newCollectorCreated) - { - try - { - Thread.sleep(200); - } - catch (InterruptedException e) - { - } - } - - return collector.list(); - } - - /** - * This method disposes all ServiceCollector instances which have been - * created by calls to method <code>list(type)</code>. - * - * @see #list - */ - private void disposeServiceCollectors() - { - if (logger.isDebugEnabled()) - logger.debug("disposeServiceCollectors()"); - synchronized (serviceCollectors) - { - for (Iterator<ServiceCollector> i = serviceCollectors.values().iterator(); i.hasNext();) - { - ServiceCollector collector = i.next(); - removeServiceListener(collector.type, collector); - } - serviceCollectors.clear(); - } - } - - /** - * Instances of ServiceCollector are used internally to speed up the - * performance of method <code>list(type)</code>. - * - * @see #list - */ - private static class ServiceCollector implements ServiceListener - { - - /** - * A set of collected service instance names. - */ - private Map<String, ServiceInfo> infos = Collections.synchronizedMap(new HashMap<String, ServiceInfo>()); - - public String type; - - public ServiceCollector(String type) - { - this.type = type; - } - - /** - * A service has been added. - */ - public void serviceAdded(ServiceEvent event) - { - synchronized (infos) - { - event.getDNS().requestServiceInfo( - event.getType(), event.getName(), 0); - } - } - - /** - * A service has been removed. - */ - public void serviceRemoved(ServiceEvent event) - { - synchronized (infos) - { - infos.remove(event.getName()); - } - } - - /** - * A service hase been resolved. Its details are now available in the - * ServiceInfo record. - */ - public void serviceResolved(ServiceEvent event) - { - synchronized (infos) - { - infos.put(event.getName(), event.getInfo()); - } - } - - /** - * Returns an array of all service infos which have been collected by this - * ServiceCollector. - * @return - */ - public ServiceInfo[] list() - { - synchronized (infos) - { - return infos.values(). - toArray(new ServiceInfo[infos.size()]); - } - } - - @Override - public String toString() - { - StringBuffer aLog = new StringBuffer(); - synchronized (infos) - { - for (Map.Entry<String, ServiceInfo> entry : infos.entrySet()) - { - aLog.append("\n\t\tService: " + entry.getKey() + ": " - + entry.getValue()); - } - } - return aLog.toString(); - } - }; - - private static String toUnqualifiedName(String type, String qualifiedName) - { - if (qualifiedName.endsWith(type)) - { - return qualifiedName.substring(0, - qualifiedName.length() - type.length() - 1); - } - else - { - return qualifiedName; - } - } - - /** - * SC-Bonjour Implementation : Method used to update the corresponding DNS - * entry in the cache of JmDNS with the new information in this ServiceInfo. - * A call to getLocalService must first be issued to get the - * ServiceInfo object to be modified. - * THIS METHOD MUST BE USED INSTEAD OF ANY DIRECT ACCESS TO JMDNS' CACHE!! - * This is used in the implementation of Zeroconf in SIP Communicator - * to be able to change fields declared by the local contact (status, etc). - * @param info Updated service data to be used to replace the old - * stuff contained in JmDNS' cache - * @param old info bytes - */ - public void updateInfos(ServiceInfo info, byte[] old) - { - - DNSOutgoing out, out2; - synchronized (JmDNS.this) - { - //list = new ArrayList(services.values()); - services.put(info.getQualifiedName().toLowerCase(), info); - } - - synchronized (info) - { - if (logger.isDebugEnabled()) - logger.debug("updateInfos() JmDNS updating " + - info.getQualifiedName() + " state " + - info.getState()); - - out = new DNSOutgoing( - /*DNSConstants.FLAGS_QR_RESPONSE*/ - DNSConstants.FLAGS_RA | DNSConstants.FLAGS_AA); - out2 = new DNSOutgoing( - /*DNSConstants.FLAGS_QR_RESPONSE*/ - DNSConstants.FLAGS_RA | DNSConstants.FLAGS_AA); - - - try - { - //out.addAnswer(new DNSRecord.Pointer(info.type, DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, info.getQualifiedName()), 0); - //out.addAnswer(new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_A, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.priority, info.weight, info.port, localHost.getName()), 0); - //out.addAnswer(new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.priority, info.weight, info.port, localHost.getName()), 0); -// out.addAnswer( -// new DNSRecord.Text( -// info.getQualifiedName(), -// DNSConstants.TYPE_TXT, -// DNSConstants.CLASS_IN , -// DNSConstants.DNS_TTL, -// info.text), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN , - 0, - old), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text), 0); - - out2.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text), 0); - - if (logger.isDebugEnabled()) - logger.debug("updateInfos() JmDNS updated infos for "+info); - - send(out); - Thread.sleep(1000); - send(out2); - Thread.sleep(2000); - send(out2); - } - catch( Exception e) - { - logger.warn( "", e); - } - } - } - - - /** - * SC-Bonjour Implementation: Method to retrieve the DNS Entry corresponding to a service - * that has been declared and return it as a ServiceInfo structure. - * It is used in the implementation of Bonjour in SIP Communicator to retrieve the information - * concerning the service declared by the local contact. THIS METHOD MUST BE USED INSTEAD OF ANY - * LOCAL COPY SAVED BEFORE SERVICE REGISTRATION!! - * @return information corresponding to the specified service - * @param FQN String representing the Fully Qualified name of the service we want info about - */ - public ServiceInfo getLocalService(String FQN) - { - return services.get(FQN); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java deleted file mode 100644 index ff922d1..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; -import java.util.logging.*; - -/** - * ServiceEvent. - * - * @author Werner Randelshofer, Rick Blair - * @version %I%, %G% - */ -public class ServiceEvent - extends EventObject -{ - private static Logger logger = - Logger.getLogger(ServiceEvent.class.toString()); - /** - * The type name of the service. - */ - private String type; - /** - * The instance name of the service. Or null, if the event was - * fired to a service type listener. - */ - private String name; - /** - * The service info record, or null if the service could be be resolved. - * This is also null, if the event was fired to a service type listener. - */ - private ServiceInfo info; - - /** - * Creates a new instance. - * - * @param source the JmDNS instance which originated the event. - * @param type the type name of the service. - * @param name the instance name of the service. - * @param info the service info record, or null if the - * service could be be resolved. - */ - public ServiceEvent(JmDNS source, String type, String name, ServiceInfo info) - { - super(source); - this.type = type; - this.name = name; - this.info = info; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Returns the JmDNS instance which originated the event. - * @return Returns the JmDNS instance which originated the event. - */ - public JmDNS getDNS() - { - return (JmDNS) getSource(); - } - - /** - * Returns the fully qualified type of the service. - * @return Returns the fully qualified type of the service. - */ - public String getType() - { - return type; - } - - /** - * Returns the instance name of the service. - * Always returns null, if the event is sent to a service type listener. - * @return Returns the instance name of the service. - */ - public String getName() - { - return name; - } - - /** - * Returns the service info record, or null if the service could not be - * resolved. - * Always returns null, if the event is sent to a service type listener. - * @return Returns the service info record. - */ - public ServiceInfo getInfo() - { - return info; - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append("<" + getClass().getName() + "> "); - buf.append(super.toString()); - buf.append(" name "); - buf.append(getName()); - buf.append(" type "); - buf.append(getType()); - buf.append(" info "); - buf.append(getInfo()); - return buf.toString(); - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java deleted file mode 100644 index b7f7f2d..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.*; - -/** - * JmDNS service information. - * - * @version %I%, %G% - * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer - * @author Christian Vincenot - */ -public class ServiceInfo implements DNSListener -{ - private static Logger logger = - Logger.getLogger(ServiceInfo.class.toString()); - public final static byte[] NO_VALUE = new byte[0]; - JmDNS dns; - - // State machine - /** - * The state of this service info. - * This is used only for services announced by JmDNS. - * <p/> - * For proper handling of concurrency, this variable must be - * changed only using methods advanceState(), revertState() and cancel(). - */ - private DNSState state = DNSState.PROBING_1; - - /** - * Task associated to this service info. - * Possible tasks are JmDNS.Prober, JmDNS.Announcer, JmDNS.Responder, - * JmDNS.Canceler. - */ - TimerTask task; - - String type; - private String name; - String server; - int port; - int weight; - int priority; - byte text[]; - private Map<String, Object> props; - InetAddress addr; - - - /** - * Construct a service description for registrating with JmDNS. - * - * @param type fully qualified service type name, - * such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, - * such as <code>foobar</code> - * @param port the local port on which the service runs - * @param text string describing the service - */ - public ServiceInfo(String type, String name, int port, String text) - { - this(type, name, port, 0, 0, text); - } - - /** - * Construct a service description for registrating with JmDNS. - * - * @param type fully qualified service type name, - * such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, - * such as <code>foobar</code> - * @param port the local port on which the service runs - * @param weight weight of the service - * @param priority priority of the service - * @param text string describing the service - */ - public ServiceInfo(String type, String name, - int port, int weight, - int priority, String text) - { - this(type, name, port, weight, priority, (byte[]) null); - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(text.length()); - writeUTF(out, text); - this.text = out.toByteArray(); - } - catch (IOException e) - { - throw new RuntimeException("unexpected exception: " + e); - } - } - - /** - * Construct a service description for registrating with JmDNS. The properties hashtable must - * map property names to either Strings or byte arrays describing the property values. - * - * @param type fully qualified service type name, such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, such as <code>foobar</code> - * @param port the local port on which the service runs - * @param weight weight of the service - * @param priority priority of the service - * @param props properties describing the service - */ - public ServiceInfo(String type, String name, - int port, int weight, - int priority, Map<String, Object> props) - { - this(type, name, port, weight, priority, new byte[0]); - if (props != null) - { - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(256); - for (Map.Entry<String, Object> prop : props.entrySet()) - { - String key = prop.getKey(); - Object val = prop.getValue(); - ByteArrayOutputStream out2 = new ByteArrayOutputStream(100); - writeUTF(out2, key); - if (val instanceof String) - { - out2.write('='); - writeUTF(out2, (String) val); - } - else - { - if (val instanceof byte[]) - { - out2.write('='); - byte[] bval = (byte[]) val; - out2.write(bval, 0, bval.length); - } - else - { - if (val != NO_VALUE) - { - throw new IllegalArgumentException( - "invalid property value: " + val); - } - } - } - byte data[] = out2.toByteArray(); - out.write(data.length); - out.write(data, 0, data.length); - } - this.text = out.toByteArray(); - } - catch (IOException e) - { - throw new RuntimeException("unexpected exception: " + e); - } - } - } - - /** - * Construct a service description for registrating with JmDNS. - * - * @param type fully qualified service type name, - * such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, - * such as <code>foobar</code> - * @param port the local port on which the service runs - * @param weight weight of the service - * @param priority priority of the service - * @param text bytes describing the service - */ - public ServiceInfo(String type, String name, - int port, int weight, - int priority, byte text[]) - { - this.type = type; - this.name = name; - this.port = port; - this.weight = weight; - this.priority = priority; - this.text = text; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Construct a service record during service discovery. - */ - ServiceInfo(String type, String name) - { - if (!type.endsWith(".")) - { - throw new IllegalArgumentException( - "type must be fully qualified DNS name ending in '.': " + type); - } - - this.type = type; - this.name = name; - } - - /** - * During recovery we need to duplicate service info to reregister them - */ - ServiceInfo(ServiceInfo info) - { - if (info != null) - { - this.type = info.type; - this.name = info.name; - this.port = info.port; - this.weight = info.weight; - this.priority = info.priority; - this.text = info.text; - } - } - - /** - * Fully qualified service type name, - * such as <code>_http._tcp.local.</code> . - * @return Returns fully qualified service type name. - */ - public String getType() - { - return type; - } - - /** - * Unqualified service instance name, - * such as <code>foobar</code> . - * @return Returns unqualified service instance name. - */ - public String getName() - { - return name; - } - - /** - * Sets the service instance name. - * - * @param name unqualified service instance name, - * such as <code>foobar</code> - */ - void setName(String name) - { - this.name = name; - } - - /** - * Fully qualified service name, - * such as <code>foobar._http._tcp.local.</code> . - * @return Returns fully qualified service name. - */ - public String getQualifiedName() - { - return name + "." + type; - } - - /** - * Get the name of the server. - * @return Returns name of the server. - */ - public String getServer() - { - return server; - } - - /** - * Get the host address of the service (ie X.X.X.X). - * @return Returns host address of the service. - */ - public String getHostAddress() - { - return (addr != null ? addr.getHostAddress() : ""); - } - - public InetAddress getAddress() - { - return addr; - } - - /** - * Get the InetAddress of the service. - * @return Returns the InetAddress of the service. - */ - public InetAddress getInetAddress() - { - return addr; - } - - /** - * Get the port for the service. - * @return Returns port for the service. - */ - public int getPort() - { - return port; - } - - /** - * Get the priority of the service. - * @return Returns the priority of the service. - */ - public int getPriority() - { - return priority; - } - - /** - * Get the weight of the service. - * @return Returns the weight of the service. - */ - public int getWeight() - { - return weight; - } - - /** - * Get the text for the serivce as raw bytes. - * @return Returns the text for the serivce as raw bytes. - */ - public byte[] getTextBytes() - { - return text; - } - - /** - * Get the text for the service. This will interpret the text bytes - * as a UTF8 encoded string. Will return null if the bytes are not - * a valid UTF8 encoded string. - * @return Returns the text for the service. - */ - public String getTextString() - { - if ((text == null) || - (text.length == 0) || - ((text.length == 1) && (text[0] == 0))) - { - return null; - } - return readUTF(text, 0, text.length); - } - - /** - * Get the URL for this service. An http URL is created by - * combining the address, port, and path properties. - * @return Returns the URL for this service. - */ - public String getURL() - { - return getURL("http"); - } - - /** - * Get the URL for this service. An URL is created by - * combining the protocol, address, port, and path properties. - * @param protocol - * @return Returns URL for this service. - */ - public String getURL(String protocol) - { - String url = protocol + "://" + getHostAddress() + ":" + getPort(); - String path = getPropertyString("path"); - if (path != null) - { - if (path.indexOf("://") >= 0) - { - url = path; - } - else - { - url += path.startsWith("/") ? path : "/" + path; - } - } - return url; - } - - /** - * Get a property of the service. This involves decoding the - * text bytes into a property list. Returns null if the property - * is not found or the text data could not be decoded correctly. - * @param name - * @return Returns property of the service as bytes. - */ - public synchronized byte[] getPropertyBytes(String name) - { - return (byte[]) getProperties().get(name); - } - - /** - * Get a property of the service. This involves decoding the - * text bytes into a property list. Returns null if the property - * is not found, the text data could not be decoded correctly, or - * the resulting bytes are not a valid UTF8 string. - * @param name - * @return Returns property of the service as string. - */ - public synchronized String getPropertyString(String name) - { - byte data[] = (byte[]) getProperties().get(name); - - if (data == null) - { - return null; - } - if (data == NO_VALUE) - { - return "true"; - } - String res = readUTF(data, 0, data.length); - - return res; - } - - /** - * Iterator<String> of the property names. - * @return Iterator<String> of the property names. - */ - public Iterator<String> getPropertyNames() - { - Map<String, Object> properties = getProperties(); - Iterable<String> propertyNames - = (properties != null) ? properties.keySet() : new Vector<String>(); - return propertyNames.iterator(); - } - - /** - * Write a UTF string with a length to a stream. - */ - void writeUTF(OutputStream out, String str) throws IOException - { - for (int i = 0, len = str.length(); i < len; i++) - { - int c = str.charAt(i); - if ((c >= 0x0001) && (c <= 0x007F)) - { - out.write(c); - } - else - { - if (c > 0x07FF) - { - out.write(0xE0 | ((c >> 12) & 0x0F)); - out.write(0x80 | ((c >> 6) & 0x3F)); - out.write(0x80 | ((c >> 0) & 0x3F)); - } - else - { - out.write(0xC0 | ((c >> 6) & 0x1F)); - out.write(0x80 | ((c >> 0) & 0x3F)); - } - } - } - } - - /** - * Read data bytes as a UTF stream. - */ - String readUTF(byte data[], int off, int len) - { - StringBuffer buf = new StringBuffer(); - for (int end = off + len; off < end;) - { - int ch = data[off++] & 0xFF; - switch (ch >> 4) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - break; - case 12: - case 13: - if (off >= len) - { - return null; - } - // 110x xxxx 10xx xxxx - ch = ((ch & 0x1F) << 6) | (data[off++] & 0x3F); - break; - case 14: - if (off + 2 >= len) - { - return null; - } - // 1110 xxxx 10xx xxxx 10xx xxxx - ch = ((ch & 0x0f) << 12) | - ((data[off++] & 0x3F) << 6) | - (data[off++] & 0x3F); - break; - default: - if (off + 1 >= len) - { - return null; - } - // 10xx xxxx, 1111 xxxx - ch = ((ch & 0x3F) << 4) | (data[off++] & 0x0f); - break; - } - buf.append((char) ch); - } - return buf.toString(); - } - - synchronized Map<String, Object> getProperties() - { - if ((props == null) && (text != null)) - { - Map<String, Object> props = new Hashtable<String, Object>(); - int off = 0; - while (off < text.length) - { - // length of the next key value pair - int len = text[off++] & 0xFF; - if ((len == 0) || (off + len > text.length)) - { - props.clear(); - break; - } - // look for the '=' - int i = 0; - for (; (i < len) && (text[off + i] != '='); i++) - { - ; - } - - // get the property name - String name = readUTF(text, off, i); - if (name == null) - { - props.clear(); - break; - } - if (i == len) - { - props.put(name, NO_VALUE); - } - else - { - byte value[] = new byte[len - ++i]; - System.arraycopy(text, off + i, value, 0, len - i); - props.put(name, value); - off += len; - } - } - this.props = props; - } - return props; - } - - - /** - * JmDNS callback to update a DNS record. - * @param rec - */ - public void updateRecord(JmDNS jmdns, long now, DNSRecord rec) - { - if ((rec != null) && !rec.isExpired(now)) - { - switch (rec.type) - { - case DNSConstants.TYPE_A: // IPv4 - case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested - if (rec.name.equals(server)) - { - addr = ((DNSRecord.Address) rec).getAddress(); - - } - break; - case DNSConstants.TYPE_SRV: - if (rec.name.equals(getQualifiedName())) - { - DNSRecord.Service srv = (DNSRecord.Service) rec; - server = srv.server; - port = srv.port; - weight = srv.weight; - priority = srv.priority; - addr = null; - // changed to use getCache() instead - jeffs - // updateRecord(jmdns, now, (DNSRecord)jmdns.cache.get(server, TYPE_A, CLASS_IN)); - updateRecord(jmdns, - now, - (DNSRecord) jmdns.getCache().get( - server, - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN)); - } - break; - case DNSConstants.TYPE_TXT: - if (rec.name.equals(getQualifiedName())) - { - DNSRecord.Text txt = (DNSRecord.Text) rec; - text = txt.text; - } - break; - } - // Future Design Pattern - // This is done, to notify the wait loop in method - // JmDNS.getServiceInfo(type, name, timeout); - if (hasData() && dns != null) - { - dns.handleServiceResolved(this); - dns = null; - } - synchronized (this) - { - notifyAll(); - } - } - } - - /** - * Returns true if the service info is filled with data. - */ - boolean hasData() - { - return server != null && addr != null && text != null; - } - - - // State machine - /** - * Sets the state and notifies all objects that wait on the ServiceInfo. - */ - synchronized void advanceState() - { - state = state.advance(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on the ServiceInfo. - */ - synchronized void revertState() - { - state = state.revert(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on the ServiceInfo. - */ - synchronized void cancel() - { - state = DNSState.CANCELED; - notifyAll(); - } - - /** - * Returns the current state of this info. - */ - DNSState getState() - { - return state; - } - - - @Override - public int hashCode() - { - return getQualifiedName().hashCode(); - } - - @Override - public boolean equals(Object obj) - { - return (obj instanceof ServiceInfo) && - getQualifiedName().equals(((ServiceInfo) obj).getQualifiedName()); - } - - public String getNiceTextString() - { - StringBuffer buf = new StringBuffer(); - for (int i = 0, len = text.length; i < len; i++) - { - if (i >= 20) - { - buf.append("..."); - break; - } - int ch = text[i] & 0xFF; - if ((ch < ' ') || (ch > 127)) - { - buf.append("\\0"); - buf.append(Integer.toString(ch, 8)); - } - else - { - buf.append((char) ch); - } - } - return buf.toString(); - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append("service["); - buf.append(getQualifiedName()); - buf.append(','); - buf.append(getAddress()); - buf.append(':'); - buf.append(port); - buf.append(','); - buf.append(getNiceTextString()); - buf.append(']'); - return buf.toString(); - } - - /** - * SC-Bonjour Implementation: Method used to set the properties of an existing ServiceInfo. - * This is used in the implementation of Bonjour in SIP Communicator to be able to replace - * old properties of the service we've declared to announce the local user with new properties - * (for example in case of a status change). - * @param props Hashtable containing all the new properties to set - */ - public void setProps(Map<String, Object> props) - { - if (props != null) - { - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(256); - for (Map.Entry<String, Object> prop : props.entrySet()) - { - String key = prop.getKey(); - Object val = prop.getValue(); - - ByteArrayOutputStream out2 = new ByteArrayOutputStream(100); - writeUTF(out2, key); - if (val instanceof String) - { - out2.write('='); - writeUTF(out2, (String) val); - } - else - { - if (val instanceof byte[]) - { - out2.write('='); - byte[] bval = (byte[]) val; - out2.write(bval, 0, bval.length); - } - else - { - if (val != NO_VALUE) - { - throw new IllegalArgumentException( - "invalid property value: " + val); - } - } - } - byte data[] = out2.toByteArray(); - out.write(data.length); - out.write(data, 0, data.length); - } - this.text = out.toByteArray(); - } - catch (IOException e) - { - throw new RuntimeException("unexpected exception: " + e); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java deleted file mode 100644 index 1c34adf..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; - -/** - * Listener for service updates. - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer - */ -public interface ServiceListener extends EventListener -{ - /** - * A service has been added. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - - void serviceAdded(ServiceEvent event); - - /** - * A service has been removed. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - void serviceRemoved(ServiceEvent event); - - /** - * A service has been resolved. Its details are now available in the - * ServiceInfo record. - * - * @param event The ServiceEvent providing the name, the fully qualified - * type of the service, and the service info record, - * or null if the service could not be resolved. - */ - - void serviceResolved(ServiceEvent event); -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java deleted file mode 100644 index 84e5c59..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; - -/** - * Listener for service types. - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer - */ -public interface ServiceTypeListener extends EventListener -{ - /** - * A new service type was discovered. - * - * @param event The service event providing the fully qualified type of - * the service. - */ - void serviceTypeAdded(ServiceEvent event); -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/zeroconf.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/zeroconf/zeroconf.provider.manifest.mf deleted file mode 100644 index 24daba0..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/zeroconf.provider.manifest.mf +++ /dev/null @@ -1,12 +0,0 @@ -Bundle-Activator: net.java.sip.communicator.impl.protocol.zeroconf.ZeroconfActivator -Bundle-Name: Zeroconf Protocol Provider -Bundle-Description: A bundle providing support for the Zeroconf protocol. -Bundle-Vendor: jitsi.org -Bundle-Version: 0.0.1 -Bundle-SymbolicName: net.java.sip.communicator.protocol.zeroconf -Import-Package: org.osgi.framework, - org.jitsi.service.configuration, - org.jitsi.service.resources, net.java.sip.communicator.service.resources, - net.java.sip.communicator.util, - net.java.sip.communicator.service.protocol, - net.java.sip.communicator.service.protocol.event |