diff options
author | Damian Minkov <damencho@jitsi.org> | 2010-08-10 07:23:45 +0000 |
---|---|---|
committer | Damian Minkov <damencho@jitsi.org> | 2010-08-10 07:23:45 +0000 |
commit | 9c1c17dff9ae4b6a803b48e5203311a0f3f94b08 (patch) | |
tree | 72fdd92f39cd0012ee2312c9596399a5fd2b07b1 | |
parent | 411c4d9f013331950967665d41b103be6ebe8cba (diff) | |
download | jitsi-9c1c17dff9ae4b6a803b48e5203311a0f3f94b08.zip jitsi-9c1c17dff9ae4b6a803b48e5203311a0f3f94b08.tar.gz jitsi-9c1c17dff9ae4b6a803b48e5203311a0f3f94b08.tar.bz2 |
Merge changes from branch/gsoc10/xcap and patch sent to dev mailinglist with subject "XCAP tests" which represents the work of Grigorii Balutsel on the "XCAP Support" GSoC 2010 project into trunk.
98 files changed, 12623 insertions, 317 deletions
@@ -897,6 +897,7 @@ bundle-plugin-icqaccregwizz,bundle-plugin-jabberaccregwizz, bundle-plugin-msnaccregwizz,bundle-plugin-sipaccregwizz, bundle-plugin-yahooaccregwizz,bundle-plugin-aimaccregwizz, + bundle-httpcore,bundle-httpclient, bundle-version,bundle-version-impl,bundle-shutdown-timeout, bundle-growlnotification,bundle-swingnotification,bundle-galagonotification, bundle-sparkle, bundle-plugin-branding, bundle-audionotifier, @@ -1909,7 +1910,7 @@ javax.swing.event, javax.swing.border"/> <!--BUNDLE-HTTPCORE --> <target name="bundle-httpcore"> - <jar compress="true" destfile="lib/bundle/httpcore.jar" + <jar compress="true" destfile="${bundles.dest}/httpcore.jar" filesetmanifest="merge"> <zipfileset src="${lib.noinst}/httpcore-4.0.1.jar" prefix=""/> <manifest> @@ -1935,6 +1936,52 @@ org.apache.http.util"/> </jar> </target> + <!--BUNDLE-HTTPCLIENT --> + <target name="bundle-httpclient"> + <jar compress="true" destfile="${bundles.dest}/httpclient.jar" + filesetmanifest="merge"> + <zipfileset src="${lib.noinst}/httpclient-4.0.1.jar" prefix=""/> + <manifest> + <attribute name="Export-Package" + value="org.apache.http.annotation, +org.apache.http.auth, +org.apache.http.auth.params, +org.apache.http.client, +org.apache.http.client.entity, +org.apache.http.client.methods, +org.apache.http.client.params, +org.apache.http.client.protocol, +org.apache.http.client.utils, +org.apache.http.conn, +org.apache.http.conn.params, +org.apache.http.conn.routing, +org.apache.http.conn.scheme, +org.apache.http.conn.ssl, +org.apache.http.conn.util, +org.apache.http.impl.client"/> + <attribute name="Import-Package" + value="org.apache.commons.logging, +javax.net.ssl, +javax.security.auth.x500, +org.apache.http, +org.apache.http.entity, +org.apache.http.impl, +org.apache.http.impl.entity, +org.apache.http.impl.io, +org.apache.http.io, +org.apache.http.message, +org.apache.http.params, +org.apache.http.protocol, +org.apache.http.util"/> + <attribute name="Bundle-Name" + value="Apache HttpComponents Client"/> + <attribute name="Bundle-Description" + value="A set of low level HTTP transport components."/> + <attribute name="System-Bundle" value="yes"/> + </manifest> + </jar> + </target> + <!-- BUNDLE-NOTIFICATION --> <target name="bundle-notification"> <!-- Creates a bundle for the notifications.--> diff --git a/lib/accounts.properties.template b/lib/accounts.properties.template index 5dd1ac9..4e3b537 100644 --- a/lib/accounts.properties.template +++ b/lib/accounts.properties.template @@ -85,6 +85,9 @@ accounts.sip.account1.PROXY_ADDRESS= # (Optional) accounts.sip.account1.PROXY_PORT= +# The XCAP server uri +# (Optional) +accounts.sip.account1.XCAP_SERVER= # ACCOUNT 2 # The user id needed to log onto the server specified in the SERVER_ADDRESS @@ -113,6 +116,10 @@ accounts.sip.account2.PROXY_ADDRESS= # (Optional) accounts.sip.account2.PROXY_PORT= +# The XCAP server uri +# (Optional) +accounts.sip.account2.XCAP_SERVER= + # This will be the contact list which we will use to test persistent storing # of sip protocol accounts.sip.CONTACT_LIST= diff --git a/lib/bundle/httpcore.jar b/lib/bundle/httpcore.jar Binary files differdeleted file mode 100644 index 0dc8446..0000000 --- a/lib/bundle/httpcore.jar +++ /dev/null diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties index 4a7081e..d373ab8 100644 --- a/lib/felix.client.run.properties +++ b/lib/felix.client.run.properties @@ -57,7 +57,8 @@ felix.auto.start.49= \ reference:file:sc-bundles/zrtp4j.jar \ reference:file:sc-bundles/protocol.jar \ reference:file:sc-bundles/protocol-media.jar \ - reference:file:lib/bundle/httpcore.jar \ + reference:file:sc-bundles/httpcore.jar \ + reference:file:sc-bundles/httpclient.jar \ reference:file:sc-bundles/globalproxyconfig.jar #the contact list service depends on protocol.jar diff --git a/lib/felix.unit.test.properties b/lib/felix.unit.test.properties index 36becac..7d6585b 100644 --- a/lib/felix.unit.test.properties +++ b/lib/felix.unit.test.properties @@ -69,7 +69,8 @@ felix.auto.start.5= \ reference:file:sc-bundles/zrtp4j.jar \ reference:file:sc-bundles/protocol.jar \ reference:file:sc-bundles/protocol-media.jar \ - reference:file:lib/bundle/httpcore.jar + reference:file:sc-bundles/httpcore.jar \ + reference:file:sc-bundles/httpclient.jar felix.auto.start.6= \ reference:file:sc-bundles/contactlist.jar \ diff --git a/lib/installer-exclude/httpclient-4.0-beta2.jar b/lib/installer-exclude/httpclient-4.0-beta2.jar Binary files differdeleted file mode 100644 index e773ccc..0000000 --- a/lib/installer-exclude/httpclient-4.0-beta2.jar +++ /dev/null diff --git a/lib/installer-exclude/httpclient-4.0.1.jar b/lib/installer-exclude/httpclient-4.0.1.jar Binary files differnew file mode 100644 index 0000000..e9c961f --- /dev/null +++ b/lib/installer-exclude/httpclient-4.0.1.jar diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 93b529b..74f2156 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -727,6 +727,15 @@ plugin.sipaccregwizz.DISPLAY_NAME=Display name plugin.sipaccregwizz.EXISTING_ACCOUNT=Existing SIP account
plugin.sipaccregwizz.CREATE_ACCOUNT=Create a free SIP account
plugin.sipaccregwizz.CREATE_ACCOUNT_TITLE=Create account
+plugin.sipaccregwizz.XCAP_OPTIONS=XCAP Options
+plugin.sipaccregwizz.XCAP_ENABLE=Enable XCAP stored contacts
+plugin.sipaccregwizz.XCAP_USE_SIP_CREDETIALS=Use SIP credetials
+plugin.sipaccregwizz.XCAP_USE_SIP_CREDETIALS_SUMMARY=XCAP use SIP credetials
+plugin.sipaccregwizz.XCAP_USER=User
+plugin.sipaccregwizz.XCAP_USER_SUMMARY=XCAP user
+plugin.sipaccregwizz.XCAP_PASSWORD=Password
+plugin.sipaccregwizz.XCAP_SERVER_URI=Server uri
+plugin.sipaccregwizz.XCAP_SERVER_URI_SUMMARY=XCAP server uri
# ssh accregwizz
plugin.sshaccregwizz.PROTOCOL_NAME=SSH
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 64739dc..5df0050 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java @@ -6,10 +6,13 @@ */ package net.java.sip.communicator.impl.protocol.sip; -import java.util.*; - +import net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists.*; import net.java.sip.communicator.service.protocol.*; +import org.w3c.dom.*; +import javax.xml.namespace.*; +import java.util.*; + /** * A simple, straightforward implementation of a SIP ContactGroup. Since * the SIP protocol is not a real one, we simply store all group details @@ -19,17 +22,13 @@ import net.java.sip.communicator.service.protocol.*; * the encapsulated object. * * @author Emil Ivov + * @author Grigorii Balutsel */ public class ContactGroupSipImpl implements ContactGroup { /** - * The name of this SIP contact group. - */ - private String groupName = null; - - /** * The list of this group's members. */ private Vector<Contact> contacts = new Vector<Contact>(); @@ -37,8 +36,7 @@ public class ContactGroupSipImpl /** * The list of sub groups belonging to this group. */ - private Vector<ContactGroup> subGroups - = new Vector<ContactGroup>(); + private Vector<ContactGroup> subGroups = new Vector<ContactGroup>(); /** * The group that this group belongs to (or null if this is the root group). @@ -75,6 +73,11 @@ public class ContactGroupSipImpl private static final String UID_SUFFIX = ".uid"; /** + * The XCAP equivalent of SIP contact group. + */ + private final ListType list; + + /** * Creates a ContactGroupSipImpl with the specified name. * * @param groupName the name of the group. @@ -84,12 +87,53 @@ public class ContactGroupSipImpl String groupName, ProtocolProviderServiceSipImpl parentProvider) { - this.groupName = groupName; - this.uid = groupName + UID_SUFFIX; + this.list = new ListType(); + this.list.setName(groupName); + this.uid = list.getName() + UID_SUFFIX; this.parentProvider = parentProvider; } /** + * Gets the list. + * + * @return the list. + */ + ListType getList() + { + return list; + } + + /** + * Sets the list custom attributes. + * + * @param otherAttributes the custom attributes. + */ + void setOtherAttributes(Map<QName, String> otherAttributes) + { + this.list.setAnyAttributes(otherAttributes); + } + + /** + * Sets the list custom elements. + * + * @param any the custom elemets. + */ + void setAny(List<Element> any) + { + this.list.setAny(any); + } + + /** + * Sets the list name. + * + * @param newName the name. + */ + void setName(String newName) + { + this.list.setName(newName); + } + + /** * Determines whether the group may contain subgroups or not. * * @return always true in this implementation. @@ -129,6 +173,10 @@ public class ContactGroupSipImpl { this.contacts.add(contactToAdd); contactToAdd.setParentGroup(this); + if(contactToAdd.isPersistent()) + { + this.list.getEntries().add(contactToAdd.getEntry()); + } } /** @@ -155,16 +203,6 @@ public class ContactGroupSipImpl } /** - * Adds the specified contact group to the contained by this group. - * @param subgroup the ContactGroupSipImpl to add as a subgroup to this group. - */ - public void addSubgroup(ContactGroupSipImpl subgroup) - { - this.subGroups.add(subgroup); - subgroup.setParentGroup(this); - } - - /** * Sets the group that is the new parent of this group * @param parent ContactGroupSipImpl */ @@ -185,6 +223,20 @@ public class ContactGroupSipImpl } /** + * Adds the specified contact group to the contained by this group. + * @param subgroup the ContactGroupSipImpl to add as a subgroup to this group. + */ + public void addSubgroup(ContactGroupSipImpl subgroup) + { + this.subGroups.add(subgroup); + subgroup.setParentGroup(this); + if(subgroup.isPersistent()) + { + this.list.getLists().add(subgroup.getList()); + } + } + + /** * Removes the specified contact group from the this group's subgroups. * @param subgroup the ContactGroupSipImpl subgroup to remove. */ @@ -192,6 +244,10 @@ public class ContactGroupSipImpl { this.subGroups.remove(subgroup); subgroup.setParentGroup(null); + if(subgroup.isPersistent()) + { + this.list.getLists().remove(subgroup.getList()); + } } /** @@ -253,8 +309,6 @@ public class ContactGroupSipImpl return null; } - - /** * Returns the <tt>Contact</tt> with the specified address or identifier. * @@ -268,11 +322,10 @@ public class ContactGroupSipImpl while (contactsIter.hasNext()) { ContactSipImpl contact = (ContactSipImpl) contactsIter.next(); - if (contact.getAddress().equals(id)) + if (contact.getUri().equals(id) || contact.getAddress().equals(id)) { return contact; } - } return null; } @@ -305,10 +358,8 @@ public class ContactGroupSipImpl { return contactGroup; } - } return null; - } /** @@ -318,16 +369,7 @@ public class ContactGroupSipImpl */ 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; + return this.list.getName(); } /** @@ -349,6 +391,10 @@ public class ContactGroupSipImpl public void removeContact(ContactSipImpl contact) { this.contacts.remove(contact); + if(contact.isPersistent()) + { + this.list.getEntries().remove(contact.getEntry()); + } } /** @@ -386,7 +432,6 @@ public class ContactGroupSipImpl return null; } - /** * Returns a String representation of this group and the contacts it * contains (may turn out to be a relatively long string). 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 4e226cf..853b8a5 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java @@ -6,35 +6,26 @@ */ package net.java.sip.communicator.impl.protocol.sip; -import javax.sip.address.*; - +import net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists.*; import net.java.sip.communicator.service.protocol.*; +import org.w3c.dom.*; +import javax.sip.address.*; +import javax.xml.namespace.*; +import java.net.URI; +import java.util.*; + /** - * A simple, straightforward implementation of a SIP Contact. Since the SIP - * 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. + * A simple, straightforward implementation of a SIP Contact. * * @author Emil Ivov * @author Benoit Pradelle * @author Lubomir Marinov + * @author Grigorii Balutsel */ public class ContactSipImpl implements Contact -{ - - /** - * The id of the contact. - */ - private final Address sipAddress; - - /** - * The display name of the contact. - */ - private String displayName = null; +{ /** * The provider that created us. @@ -52,6 +43,16 @@ public class ContactSipImpl private PresenceStatus presenceStatus; /** + * The image uri. + */ + private URI imageUri; + + /** + * The image content. + */ + private byte[] image; + + /** * Determines whether this contact is persistent, i.e. member of the contact * list or whether it is here only temporarily. */ @@ -70,6 +71,16 @@ public class ContactSipImpl private boolean isResolvable = true; /** + * The XCAP equivalent of SIP contact group. + */ + private final EntryType entry; + + /** + * The SIP contact identifier. + */ + private final Address sipAddress; + + /** * Creates an instance of a meta contact with the specified string used * as a name and identifier. * @@ -82,19 +93,33 @@ public class ContactSipImpl ProtocolProviderServiceSipImpl parentProvider) { this.sipAddress = contactAddress; - - displayName = contactAddress.getDisplayName(); - - if(displayName == null || displayName.trim().length() == 0) - displayName = getAddress(); - + this.entry = new EntryType(contactAddress.getURI().toString()); this.parentProvider = parentProvider; - this.presenceStatus = parentProvider.getSipStatusEnum() .getStatus(SipStatusEnum.UNKNOWN); } /** + * Gets the entry. + * + * @return the entry + */ + EntryType getEntry() + { + return entry; + } + + /** + * Gets the entry's uri. + * + * @return the entry' uri. + */ + public String getUri() + { + return entry.getUri(); + } + + /** * This method is only called when the contact is added to a new * <tt>ContactGroupSipImpl</tt> by the * <tt>ContactGroupSipImpl</tt> itself. @@ -114,8 +139,7 @@ public class ContactSipImpl */ public String getAddress() { - SipURI sipURI = (SipURI)sipAddress.getURI(); - + SipURI sipURI = (SipURI) sipAddress.getURI(); return sipURI.getUser() + "@" + sipURI.getHost(); } @@ -129,7 +153,6 @@ public class ContactSipImpl return sipAddress; } - /** * Returns a String that could be used by any user interacting modules * for referring to this contact. @@ -139,7 +162,11 @@ public class ContactSipImpl */ public String getDisplayName() { - return (displayName == null) ? getAddress() : displayName; + if(this.entry.getDisplayName() != null) + { + return this.entry.getDisplayName().getValue(); + } + return getAddress(); } /** @@ -150,18 +177,82 @@ public class ContactSipImpl */ public void setDisplayName(String displayName) { - this.displayName = displayName; + DisplayNameType displayNameType = new DisplayNameType(); + displayNameType.setValue(displayName); + this.entry.setDisplayName(displayNameType); + } + + /** + * Sets a String that could be used by any user interacting modules for + * referring to this contact. + * + * @param displayName a human readable name to use for this contact. + */ + public void setDisplayName(DisplayNameType displayName) + { + this.entry.setDisplayName(displayName); + } + + /** + * Sets the entry custom attributes. + * + * @param otherAttributes the custom attributes. + */ + void setOtherAttributes(Map<QName, String> otherAttributes) + { + this.entry.setAnyAttributes(otherAttributes); + } + + /** + * Sets the entry custom elements. + * + * @param any the custom elemets. + */ + void setAny(List<Element> any) + { + this.entry.setAny(any); + } + + /** + * Gets the image uri. + * + * @return the image uri. + */ + URI getImageUri() + { + return imageUri; + } + + /** + * Sets the image uri. + * + * @param imageUri the image uri. + */ + void setImageUri(URI imageUri) + { + this.imageUri = imageUri; } /** - * Returns a byte array containing an image (most often a photo or an - * avatar) that the contact uses as a representation. + * Gets 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; + return image; + } + + /** + * Sets a byte array containing an image (most often a photo or an avatar) + * that the contact uses as a representation. + * + * @param image an image representing the contact. + */ + void setImage(byte[] image) + { + this.image = image; } /** 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 259d6af..7ad72b5 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java @@ -6,9 +6,12 @@ */ package net.java.sip.communicator.impl.protocol.sip; -import java.io.*; -import java.text.*; -import java.util.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.xml.*; +import org.w3c.dom.*; import javax.sip.*; import javax.sip.address.*; @@ -18,13 +21,10 @@ import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.stream.*; - -import org.w3c.dom.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.xml.*; +import java.io.*; +import java.net.URI; +import java.text.*; +import java.util.*; /** * Sip presence implementation (SIMPLE). @@ -34,6 +34,7 @@ import net.java.sip.communicator.util.xml.*; * @author Benoit Pradelle * @author Lubomir Marinov * @author Emil Ivov + * @author Grigorii Balutsel */ public class OperationSetPresenceSipImpl extends AbstractOperationSetPersistentPresence<ProtocolProviderServiceSipImpl> @@ -46,10 +47,8 @@ public class OperationSetPresenceSipImpl private static final Logger logger = Logger.getLogger(OperationSetPresenceSipImpl.class); - /** - * The root of the SIP contact list. - */ - private final ContactGroupSipImpl contactListRoot; + + private ServerStoredContactListSipImpl ssContactList; /** * The currently active status message. @@ -189,6 +188,8 @@ public class OperationSetPresenceSipImpl private static final String NS_BUSY_ELT = "rpid:busy"; private static final String OTP_ELEMENT = "on-the-phone"; private static final String NS_OTP_ELT = "rpid:on-the-phone"; + private static final String STATUS_ICON_ELEMENT = "status-icon"; + private static final String NS_STATUS_ICON_ELT = "rpid:status-icon"; // namespace wildcard private static final String ANY_NS = "*"; @@ -226,7 +227,10 @@ public class OperationSetPresenceSipImpl { super(provider); - this.contactListRoot = new ContactGroupSipImpl("RootGroup", provider); + //this.contactListRoot = new ContactGroupSipImpl("RootGroup", provider); + this.ssContactList = new ServerStoredContactListSipImpl(provider, this); + + //this.ssContactList.addGroupListener(); //add our registration listener this.parentProvider.addRegistrationStateChangeListener(this); @@ -300,6 +304,31 @@ public class OperationSetPresenceSipImpl } /** + * Registers a listener that would receive events upong changes in server + * stored groups. + * + * @param listener a ServerStoredGroupChangeListener impl that would receive + * events upong group changes. + */ + public void addServerStoredGroupChangeListener( + ServerStoredGroupListener listener) + { + ssContactList.addGroupListener(listener); + } + + /** + * Removes the specified group change listener so that it won't receive + * any further events. + * + * @param listener the ServerStoredGroupChangeListener to remove + */ + public void removeServerStoredGroupChangeListener( + ServerStoredGroupListener listener) + { + ssContactList.removeGroupListener(listener); + } + + /** * Returns a PresenceStatus instance representing the state this provider is * currently in. Note that PresenceStatus instances returned by this method * MUST adequately represent all possible states that a provider might @@ -339,26 +368,29 @@ public class OperationSetPresenceSipImpl */ public ContactGroup getServerStoredContactListRoot() { - return this.contactListRoot; + return this.ssContactList.getRootGroup(); } /** * 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 parentGroup the group where the new group should be created * @param groupName the name of the new group to create. */ - public void createServerStoredContactGroup(ContactGroup parent, + public void createServerStoredContactGroup(ContactGroup parentGroup, String groupName) + throws OperationFailedException { - ContactGroupSipImpl newGroup = new ContactGroupSipImpl(groupName, - this.parentProvider); - - ((ContactGroupSipImpl) parent).addSubgroup(newGroup); - - this.fireServerStoredGroupEvent(newGroup, - ServerStoredGroupEvent.GROUP_CREATED_EVENT); + if (!(parentGroup instanceof ContactGroupSipImpl)) + { + String errorMessage = String.format( + "Group %1s does not seem to belong to this protocol's " + + "contact list", parentGroup.getGroupName()); + throw new IllegalArgumentException(errorMessage); + } + ContactGroupSipImpl sipGroup = (ContactGroupSipImpl) parentGroup; + ssContactList.createGroup(sipGroup, groupName, true); } /** @@ -382,25 +414,17 @@ public class OperationSetPresenceSipImpl public ContactGroup createUnresolvedContactGroup(String groupUID, String persistentData, ContactGroup parentGroup) { - ContactGroupSipImpl newGroup = new ContactGroupSipImpl( - ContactGroupSipImpl.createNameFromUID(groupUID), - this.parentProvider); - newGroup.setResolved(false); - //if parent is null then we're adding under root. if(parentGroup == null) { parentGroup = getServerStoredContactListRoot(); } - - ((ContactGroupSipImpl) parentGroup).addSubgroup(newGroup); - - this.fireServerStoredGroupEvent( - newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT); - - return newGroup; + String groupName = ContactGroupSipImpl.createNameFromUID(groupUID); + return ssContactList.createUnresolvedContactGroup( + (ContactGroupSipImpl) parentGroup, groupName); } + /** * Renames the specified group from the server stored contact list. * @@ -410,11 +434,14 @@ public class OperationSetPresenceSipImpl public void renameServerStoredContactGroup(ContactGroup group, String newName) { - ((ContactGroupSipImpl) group).setGroupName(newName); - - this.fireServerStoredGroupEvent( - (ContactGroupSipImpl) group, - ServerStoredGroupEvent.GROUP_RENAMED_EVENT); + if (!(group instanceof ContactGroupSipImpl)) + { + String errorMessage = String.format( + "Group %1s does not seem to belong to this protocol's " + + "contact list", group.getGroupName()); + throw new IllegalArgumentException(errorMessage); + } + ssContactList.renameGroup((ContactGroupSipImpl) group, newName); } /** @@ -429,17 +456,19 @@ public class OperationSetPresenceSipImpl ContactGroup newParent) { if (!(contactToMove instanceof ContactSipImpl)) + { return; - - ContactSipImpl sipContact = (ContactSipImpl) contactToMove; - ContactGroupSipImpl parentSipGroup - = (ContactGroupSipImpl) sipContact.getParentContactGroup(); - - parentSipGroup.removeContact(sipContact); - - ((ContactGroupSipImpl) newParent).addContact(sipContact); - - fireSubscriptionMovedEvent(contactToMove, parentSipGroup, newParent); + } + try + { + ssContactList.moveContactToGroup((ContactSipImpl) contactToMove, + (ContactGroupSipImpl) newParent); + } + catch (OperationFailedException ex) + { + throw new IllegalStateException( + "Failed to move contact " + contactToMove.getAddress(), ex); + } } /** @@ -451,22 +480,16 @@ public class OperationSetPresenceSipImpl * protocol's contact list. */ public void removeServerStoredContactGroup(ContactGroup group) - throws IllegalArgumentException { - ContactGroupSipImpl sipGroup = (ContactGroupSipImpl) group; - ContactGroupSipImpl parent = contactListRoot.findGroupParent(sipGroup); - - if(parent == null){ - throw new IllegalArgumentException( - "group " + group - + " does not seem to belong to this protocol's contact list."); + if (!(group instanceof ContactGroupSipImpl)) + { + String errorMessage = String.format( + "Group %1s does not seem to belong to this protocol's " + + "contact list", group.getGroupName()); + throw new IllegalArgumentException(errorMessage); } - - parent.removeSubGroup(sipGroup); - - fireServerStoredGroupEvent( - sipGroup, - ServerStoredGroupEvent.GROUP_REMOVED_EVENT); + ContactGroupSipImpl sipGroup = (ContactGroupSipImpl) group; + ssContactList.removeGroup(sipGroup); } /** @@ -881,7 +904,7 @@ public class OperationSetPresenceSipImpl IllegalStateException, OperationFailedException { - subscribe(this.contactListRoot, contactIdentifier); + subscribe(this.ssContactList.getRootGroup(), contactIdentifier); } /** @@ -908,9 +931,16 @@ public class OperationSetPresenceSipImpl IllegalStateException, OperationFailedException { - if (logger.isDebugEnabled()) - logger.debug("let's subscribe " + contactIdentifier); + assertConnected(); + if (!(parentGroup instanceof ContactGroupSipImpl)) + { + String errorMessage = String.format( + "Group %1s does not seem to belong to this protocol's " + + "contact list", + parentGroup.getGroupName()); + throw new IllegalArgumentException(errorMessage); + } //if the contact is already in the contact list ContactSipImpl contact = resolveContactID(contactIdentifier); @@ -927,40 +957,15 @@ public class OperationSetPresenceSipImpl // we will remove it as we will created again // this is the case when making a non persistent contact to // a persistent one - ContactGroupSipImpl oldParentGroup = - (ContactGroupSipImpl)contact.getParentContactGroup(); - oldParentGroup.removeContact(contact); - fireSubscriptionEvent(contact, oldParentGroup, - SubscriptionEvent.SUBSCRIPTION_REMOVED); + ssContactList.removeContact(contact); } } - - Address contactAddress; - try - { - contactAddress - = parentProvider.parseAddressString(contactIdentifier); - } - catch (ParseException exc) + contact = ssContactList.createContact((ContactGroupSipImpl) parentGroup, + contactIdentifier, true); + if (this.presenceEnabled) { - throw new IllegalArgumentException( - contactIdentifier + " is not a valid string.", exc); + subscriber.subscribe(new PresenceSubscriberSubscription(contact)); } - - // create a new contact, marked as resolvable and non resolved - contact = new ContactSipImpl(contactAddress, this.parentProvider); - ((ContactGroupSipImpl) parentGroup).addContact(contact); - - fireSubscriptionEvent(contact, - parentGroup, - SubscriptionEvent.SUBSCRIPTION_CREATED); - - // do not query the presence state - if (this.presenceEnabled == false) - return; - - assertConnected(); - subscriber.subscribe(new PresenceSubscriberSubscription(contact)); } /** @@ -1003,28 +1008,19 @@ public class OperationSetPresenceSipImpl assertConnected(); if (!(contact instanceof ContactSipImpl)) - throw - new IllegalArgumentException( - "the contact is not a SIP contact"); - - ContactSipImpl sipcontact = (ContactSipImpl) contact; - + { + throw new IllegalArgumentException("The contact is not a SIP " + + "contact"); + } + ContactSipImpl sipContact = (ContactSipImpl) contact; /** * Does not assert if there is no subscription cause if the user * becomes offline he has terminated the subscription and so we have * no subscription of this contact but we wont to remove it. * Does not assert on connected cause have already has made the check. */ - unsubscribe(sipcontact, false); - - ((ContactGroupSipImpl) sipcontact.getParentContactGroup()) - .removeContact(sipcontact); - - // inform the listeners - fireSubscriptionEvent( - sipcontact, - sipcontact.getParentContactGroup(), - SubscriptionEvent.SUBSCRIPTION_REMOVED); + unsubscribe(sipContact, false); + ssContactList.removeContact(sipContact); } /** @@ -1542,7 +1538,7 @@ public class OperationSetPresenceSipImpl */ public ContactSipImpl findContactByID(String contactID) { - return this.contactListRoot.findContactByID(contactID); + return this.ssContactList.getRootGroup().findContactByID(contactID); } /** @@ -1647,29 +1643,8 @@ public class OperationSetPresenceSipImpl String persistentData, ContactGroup parent) { - Address contactAddress; - try - { - contactAddress = parentProvider.parseAddressString(addressStr); - } - catch (ParseException exc) - { - throw new IllegalArgumentException( - addressStr + " is not a valid string.", exc); - } - - ContactSipImpl contact = new ContactSipImpl(contactAddress, - this.parentProvider); - - contact.setResolved(false); - - ((ContactGroupSipImpl) parent).addContact(contact); - - fireSubscriptionEvent(contact, - parent, - SubscriptionEvent.SUBSCRIPTION_CREATED); - - return contact; + return ssContactList.createUnresolvedContact((ContactGroupSipImpl) + parent, addressStr); } /** @@ -1684,37 +1659,25 @@ public class OperationSetPresenceSipImpl */ public ContactSipImpl createVolatileContact(Address contactAddress) { - // First create the new volatile contact; - ContactSipImpl newVolatileContact - = new ContactSipImpl(contactAddress, this.parentProvider); - newVolatileContact.setDisplayName(contactAddress.getDisplayName()); - newVolatileContact.setPersistent(false); - - // Check whether a volatile group already exists and if not create one - ContactGroupSipImpl theVolatileGroup = getNonPersistentGroup(); - - // if the parent volatile group is null then we create it - if (theVolatileGroup == null) + try { - theVolatileGroup = new ContactGroupSipImpl( - "NotInContactList", - this.parentProvider); - theVolatileGroup.setResolved(false); - theVolatileGroup.setPersistent(false); - - this.contactListRoot.addSubgroup(theVolatileGroup); - - fireServerStoredGroupEvent(theVolatileGroup - , ServerStoredGroupEvent.GROUP_CREATED_EVENT); + // Check whether a volatile group already exists and if not create one + ContactGroupSipImpl volatileGroup = getNonPersistentGroup(); + // if the parent volatile group is null then we create it + if (volatileGroup == null) + { + ContactGroupSipImpl rootGroup = + this.ssContactList.getRootGroup(); + volatileGroup = ssContactList + .createGroup(rootGroup, "NotInContactList", false); + } + return ssContactList.createContact(volatileGroup, + contactAddress.getURI().toString(), false); + } + catch (OperationFailedException ex) + { + return null; } - - //now add the volatile contact inside it - theVolatileGroup.addContact(newVolatileContact); - fireSubscriptionEvent(newVolatileContact - , theVolatileGroup - , SubscriptionEvent.SUBSCRIPTION_CREATED); - - return newVolatileContact; } /** @@ -1929,6 +1892,17 @@ public class OperationSetPresenceSipImpl Element activities = doc.createElement(NS_ACTIVITY_ELT); person.appendChild(activities); + // <status-icon> + XCapClient xCapClient = parentProvider.getXCapClient(); + if (xCapClient.isConnected() && xCapClient.isPresContentSupported()) + { + Element statusIcon = doc.createElement(NS_STATUS_ICON_ELT); + URI imageUri = xCapClient.getPresContentImageUri( + ProtocolProviderServiceSipImpl.PRES_CONTENT_IMAGE_NAME); + statusIcon.setTextContent(imageUri.toString()); + person.appendChild(statusIcon); + } + // the correct activity if (contact.getPresenceStatus() .equals(sipStatusEnum.getStatus(SipStatusEnum.AWAY))) @@ -2046,6 +2020,7 @@ public class OperationSetPresenceSipImpl // ignore namespaces here PresenceStatus personStatus = null; + URI personStatusIcon = null; NodeList personList = presence.getElementsByTagNameNS(ANY_NS, PERSON_ELEMENT); @@ -2110,6 +2085,43 @@ public class OperationSetPresenceSipImpl break; } } + NodeList statusIconList = person.getElementsByTagNameNS(ANY_NS, + STATUS_ICON_ELEMENT); + if (statusIconList.getLength() > 0) + { + Element statusIcon; + Node statusIconNode = statusIconList.item(0); + if (statusIconNode.getNodeType() == Node.ELEMENT_NODE) + { + statusIcon = (Element) statusIconNode; + String content = getTextContent(statusIcon); + if (content != null && content.trim().length() != 0) + { + try + { + personStatusIcon = URI.create(content); + } + catch (IllegalArgumentException ex) + { + logger.error("Person's status icon uri: " + + content + " is invalid"); + } + } + } + } + } + + if(personStatusIcon != null) + { + String contactID = + XMLUtils.getAttribute(presNode, ENTITY_ATTRIBUTE); + + if (contactID.startsWith("pres:")) + { + contactID = contactID.substring("pres:".length()); + } + Contact contact = resolveContactID(contactID); + updateContactIcon((ContactSipImpl) contact, personStatusIcon); } // Vector containing the list of status to set for each contact in @@ -2397,6 +2409,61 @@ public class OperationSetPresenceSipImpl } /** + * Checks whether to URIs are equal with safe null check. + * @param uri1 to be compared. + * @param uri2 to be compared. + * @return if uri1 is equal to uri2. + */ + public static boolean isEquals(URI uri1, URI uri2) { + return (uri1 == null && uri2 == null) + || (uri1 != null && uri1.equals(uri2)); + } + + /** + * Changes the Contact image + * @param contact + * @param imageUri + */ + private void updateContactIcon(ContactSipImpl contact, URI imageUri) + { + if(isEquals(contact.getImageUri(), imageUri)) + { + return; + } + byte[] oldImage = contact.getImage(); + byte[] newImage = new byte[0]; + if(imageUri != null) + { + XCapClient xCapClient = parentProvider.getXCapClient(); + if(xCapClient.isConnected()) + { + try + { + newImage = xCapClient.getImage(imageUri); + } + catch (XCapException e) + { + String errorMessage = String.format( + "Error while getting icon %1s for the contact %2s", + imageUri, contact.getUri()); + logger.error(errorMessage, e); + } + } + else + { + return; + } + } + contact.setImageUri(imageUri); + contact.setImage(newImage); + fireContactPropertyChangeEvent( + ContactPropertyChangeEvent.PROPERTY_IMAGE, + contact, + oldImage, + newImage); + } + + /** * Secured call to XMLUtils.getText (no null returned but an empty string) * * @param node the node with which call <tt>XMLUtils.getText()</tt> @@ -2732,6 +2799,39 @@ public class OperationSetPresenceSipImpl } /** + * Will wait for every SUBSCRIBE, NOTIFY and PUBLISH transaction + * to finish before continuing the unsubscription + */ + private void stopEvents() + { + for (byte i = 0; i < 10; i++) + { + synchronized (waitedCallIds) + { + if (waitedCallIds.size() == 0) + { + break; + } + } + synchronized (this) + { + try + { + // Wait 5 s. max + wait(500); + } + catch (InterruptedException e) + { + if (logger.isDebugEnabled()) + { + logger.debug("abnormal behavior, may cause unnecessary CPU use", e); + } + } + } + } + } + + /** * The method is called by a ProtocolProvider implementation whenever * a change in the registration state of the corresponding provider had * occurred. The method is particularly interested in events stating @@ -2743,11 +2843,12 @@ public class OperationSetPresenceSipImpl */ public void registrationStateChanged(RegistrationStateChangeEvent evt) { - if(evt.getNewState() == RegistrationState.UNREGISTERING) + if (evt.getNewState().equals(RegistrationState.UNREGISTERING)) { // stop any task associated with the timer cancelTimer(); - + // Destroy XCAP contacts + ssContactList.destroy(); // this will not be called by anyone else, so call it // the method will terminate every active subscription try @@ -2759,39 +2860,18 @@ public class OperationSetPresenceSipImpl { logger.error("can't set the offline mode", e); } - - // we wait for every SUBSCRIBE, NOTIFY and PUBLISH transaction - // to finish before continuing the unsubscription - for (byte i = 0; i < 10; i++) - { // wait 5 s. max - synchronized (waitedCallIds) - { - if (waitedCallIds.size() == 0) - { - break; - } - } - - synchronized (this) - { - try - { - wait(500); - } - catch (InterruptedException e) - { - if (logger.isDebugEnabled()) - logger.debug("abnormal behavior, may cause " + - "unnecessary CPU use", e); - } - } - } + stopEvents(); } else if (evt.getNewState().equals( RegistrationState.REGISTERED)) { if (logger.isDebugEnabled()) + { logger.debug("enter registered state"); + } + + // Init XCAP contacts + ssContactList.init(); /* * If presence support is enabled and the keep-alive method diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetServerStoredAccountInfoSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetServerStoredAccountInfoSipImpl.java new file mode 100644 index 0000000..05df409 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetServerStoredAccountInfoSipImpl.java @@ -0,0 +1,450 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip; + +import net.java.sip.communicator.impl.protocol.sip.xcap.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +import java.util.*; + +/** + * SIP server stored account information. Supports the user avatar during + * pres-content specification. + * + * @author Grigorii Balutsel + */ +public class OperationSetServerStoredAccountInfoSipImpl + implements OperationSetServerStoredAccountInfo, + RegistrationStateChangeListener +{ + /** + * Logger class. + */ + private static final Logger logger = + Logger.getLogger(OperationSetServerStoredAccountInfoSipImpl.class); + + /** + * The provider that is on top of us. + */ + private ProtocolProviderServiceSipImpl provider; + + /** + * Current image. + */ + private ImageDetail accountImage; + + /** + * Flag whether account image is loaded. + */ + private boolean isAccountImageLoaded = false; + + /** + * Creates this op.set. + * @param provider the parent provider. + */ + public OperationSetServerStoredAccountInfoSipImpl( + ProtocolProviderServiceSipImpl provider) + { + this.provider = provider; + this.provider.addRegistrationStateChangeListener(this); + } + + /** + * Returns an iterator over all details of the specified class. + * + * @param detailClass one of the detail classes defined in the + * ServerStoredDetails class, indicating the kind of + * details we're interested in. + * @return a java.util.Iterator over all details that are instances or + * descendants of the specified class. + */ + public Iterator<GenericDetail> getDetailsAndDescendants( + Class<? extends GenericDetail> detailClass) + { + List<GenericDetail> result = new Vector<GenericDetail>(); + if (ImageDetail.class.isAssignableFrom(detailClass) && + isImageDetailSupported()) + { + ImageDetail imageDetail = getAccountImage(); + if (imageDetail != null) + { + result.add(getAccountImage()); + } + } + return result.iterator(); + } + + /** + * Returns an iterator over all details that are instances of exactly the + * same class as the one specified. + * + * @param detailClass one of the detail classes defined in the + * ServerStoredDetails class, indicating the kind of + * details we're interested in. + * @return a java.util.Iterator over all details of specified class. + */ + public Iterator<GenericDetail> getDetails( + Class<? extends GenericDetail> detailClass) + { + List<GenericDetail> result = new ArrayList<GenericDetail>(); + if (ImageDetail.class.isAssignableFrom(detailClass) && + isImageDetailSupported()) + { + ImageDetail imageDetail = getAccountImage(); + if (imageDetail != null) + { + result.add(getAccountImage()); + } + } + return result.iterator(); + } + + /** + * Returns all details currently available and set for our account. + * + * @return a java.util.Iterator over all details currently set our account. + */ + public Iterator<GenericDetail> getAllAvailableDetails() + { + List<GenericDetail> details = new ArrayList<GenericDetail>(); + if (isImageDetailSupported()) + { + ImageDetail imageDetail = getAccountImage(); + if (imageDetail != null) + { + details.add(getAccountImage()); + } + } + return details.iterator(); + } + + /** + * Returns all detail Class-es that the underlying implementation supports + * setting. + * + * @return a java.util.Iterator over all detail classes supported by the + * implementation. + */ + public Iterator<Class<? extends GenericDetail>> getSupportedDetailTypes() + { + List<Class<? extends GenericDetail>> result = + new Vector<Class<? extends GenericDetail>>(); + if (isImageDetailSupported()) + { + result.add(ImageDetail.class); + } + return result.iterator(); + } + + /** + * Determines whether a detail class represents a detail supported by the + * underlying implementation or not. + * + * @param detailClass the class the support for which we'd like to + * determine. + * @return true if the underlying implementation supports setting details of + * this type and false otherwise. + */ + public boolean isDetailClassSupported( + Class<? extends GenericDetail> detailClass) + { + return ImageDetail.class.isAssignableFrom(detailClass) && + isImageDetailSupported(); + } + + /** + * The method returns the number of instances supported for a particular + * detail type. + * + * @return int the maximum number of detail instances. + */ + public int getMaxDetailInstances(Class<? extends GenericDetail> detailClass) + { + if (ImageDetail.class.isAssignableFrom(detailClass) && + isImageDetailSupported()) + { + return 1; + } + return 0; + } + + /** + * Adds the specified detail to the list of details registered on-line + * for this account. + * + * @param detail the detail that we'd like registered on the server. + * @throws IllegalArgumentException if such a detail already exists + * and its max instances number has + * been atteined or if the underlying + * implementation does not support + * setting details of the + * corresponding class. + * @throws OperationFailedException with code Network Failure if + * putting the new value online has + * failed. + * @throws ArrayIndexOutOfBoundsException if the number of instances + * currently registered by the + * application is already equal to + * the maximum number of supported + * instances. + */ + public void addDetail(GenericDetail detail) + throws IllegalArgumentException, OperationFailedException, + ArrayIndexOutOfBoundsException + { + if (!isDetailClassSupported(detail.getClass())) + { + throw new IllegalArgumentException( + "Implementation does not support such details " + + detail.getClass()); + } + List<GenericDetail> alreadySetDetails = new Vector<GenericDetail>(); + Iterator<GenericDetail> iter = getDetails(detail.getClass()); + while (iter.hasNext()) + { + alreadySetDetails.add(iter.next()); + } + if (alreadySetDetails.size() >= + getMaxDetailInstances(detail.getClass())) + { + throw new ArrayIndexOutOfBoundsException( + "Max count for this detail is already reached"); + } + if (ImageDetail.class.isAssignableFrom(detail.getClass()) && + isImageDetailSupported()) + { + ImageDetail imageDetail = (ImageDetail) detail; + putImageDetail(imageDetail); + accountImage = imageDetail; + isAccountImageLoaded = true; + } + } + + /** + * Removes the specified detail from the list of details stored online for + * this account. + * + * @param detail the detail to remove + * @return true if the specified detail existed and was successfully removed + * and false otherwise. + * @throws OperationFailedException with code Network Failure if removing + * the detail from the server has failed + */ + public boolean removeDetail(GenericDetail detail) + throws OperationFailedException + { + boolean isFound = false; + Iterator<?> iter = getAllAvailableDetails(); + while (iter.hasNext()) + { + GenericDetail item = (GenericDetail) iter.next(); + if (item.equals(detail)) + { + isFound = true; + } + } + // Current detail value does not exist + if (!isFound) + { + return false; + } + if (ImageDetail.class.isAssignableFrom(detail.getClass()) && + isImageDetailSupported()) + { + deleteImageDetail(); + accountImage = null; + } + return true; + } + + /** + * Replaces the currentDetailValue detail with newDetailValue and returns + * true if the operation was a success or false if currentDetailValue did + * not previously exist (in this case an additional call to addDetail is + * required). + * + * @param currentDetailValue the detail value we'd like to replace. + * @param newDetailValue the value of the detail that we'd like to + * replace currentDetailValue with. + * @return + * @throws ClassCastException if newDetailValue is not an instance of + * the same class as currentDetailValue. + * @throws OperationFailedException with code Network Failure if putting the + * new value back online has failed + */ + public boolean replaceDetail( + GenericDetail currentDetailValue, + GenericDetail newDetailValue) + throws ClassCastException, OperationFailedException + { + if (!newDetailValue.getClass().equals(currentDetailValue.getClass())) + { + throw new ClassCastException( + "New value to be replaced is not as the current one"); + } + // If values are the same no change + if (currentDetailValue.equals(newDetailValue)) + { + return true; + } + removeDetail(currentDetailValue); + addDetail(newDetailValue); + return true; + } + + /** + * Determines if image details is supported. + * + * @return true if supported, false otherwise. + */ + private boolean isImageDetailSupported() + { + XCapClient xCapClient = provider.getXCapClient(); + return xCapClient != null && + xCapClient.isConnected() && + xCapClient.isPresContentSupported(); + } + + /** + * Gets the user avatar from the server or returns cached value. + * + * @return the image detail. + */ + private ImageDetail getAccountImage() + { + if (isAccountImageLoaded) + { + return accountImage; + } + isAccountImageLoaded = true; + try + { + accountImage = getImageDetail(); + } + catch (OperationFailedException e) + { + if (logger.isInfoEnabled()) + { + logger.info("Avatar image cannot be loaded", e); + } + } + return accountImage; + } + + /** + * Gets image detail from the XCAP server. + * + * @return the image detail. + * @throws OperationFailedException if there is some error during operation. + */ + private ImageDetail getImageDetail() throws OperationFailedException + { + ImageDetail imageDetail; + XCapClient xCapClient = provider.getXCapClient(); + try + { + ContentType presContent = xCapClient.getPresContent( + ProtocolProviderServiceSipImpl.PRES_CONTENT_IMAGE_NAME); + if (presContent == null) + { + return null; + } + String description = null; + byte[] content = null; + if (presContent.getDescription().size() > 0) + { + description = presContent.getDescription().get(0).getValue(); + } + if (presContent.getData() != null) + { + content = Base64.decode(presContent.getData().getValue()); + } + imageDetail = new ImageDetail(description, content); + } + catch (XCapException e) + { + throw new OperationFailedException("Cannot get image detail", + OperationFailedException.NETWORK_FAILURE); + } + return imageDetail; + } + + /** + * Puts the image detail to the XCAP server. + * + * @param imageDetail the image detail. + * @throws OperationFailedException if there is some error during operation. + */ + private void putImageDetail(ImageDetail imageDetail) + throws OperationFailedException + { + XCapClient xCapClient = provider.getXCapClient(); + ContentType presContent = new ContentType(); + MimeType mimeType = new MimeType(); + mimeType.setValue("image/png"); + presContent.setMimeType(mimeType); + EncodingType encoding = new EncodingType(); + encoding.setValue("base64"); + presContent.setEncoding(encoding); + String encodedImageContent = + new String(Base64.encode(imageDetail.getBytes())); + DataType data = new DataType(); + data.setValue(encodedImageContent); + presContent.setData(data); + try + { + xCapClient.putPresContent(presContent, + ProtocolProviderServiceSipImpl.PRES_CONTENT_IMAGE_NAME); + } + catch (XCapException e) + { + throw new OperationFailedException("Cannot put image detail", + OperationFailedException.NETWORK_FAILURE); + } + } + + /** + * Deletes the image detail from the XCAP server. + * + * @throws OperationFailedException if there is some error during operation. + */ + private void deleteImageDetail() + throws OperationFailedException + { + XCapClient xCapClient = provider.getXCapClient(); + try + { + xCapClient.deletePresContent( + ProtocolProviderServiceSipImpl.PRES_CONTENT_IMAGE_NAME); + } + catch (XCapException e) + { + throw new OperationFailedException("Cannot delete image detail", + OperationFailedException.NETWORK_FAILURE); + } + } + + /** + * Lister method for protocol provider registration event. If state is + * UNREGISTERED or CONNECTION_FAILED it will clear the cache. + * + * @param evt the event describing the status change. + */ + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + if (evt.getNewState().equals(RegistrationState.UNREGISTERED) || + evt.getNewState().equals(RegistrationState.CONNECTION_FAILED)) + { + isAccountImageLoaded = false; + accountImage = 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 12caa30..672c3db 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java @@ -6,26 +6,25 @@ */ package net.java.sip.communicator.impl.protocol.sip; -import java.net.*; -import java.text.*; -import java.util.*; - -import javax.sip.*; -import javax.sip.address.*; -import javax.sip.header.*; -import javax.sip.message.*; - -import org.osgi.framework.*; - +import gov.nist.javax.sip.address.*; +import gov.nist.javax.sip.header.*; +import gov.nist.javax.sip.message.*; import net.java.sip.communicator.impl.protocol.sip.security.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.service.version.Version; // avoid ambiguity with org.osgi.framework.Version +import net.java.sip.communicator.service.version.Version; import net.java.sip.communicator.util.*; +import org.osgi.framework.*; -import gov.nist.javax.sip.header.*; -import gov.nist.javax.sip.address.*; -import gov.nist.javax.sip.message.*; +import javax.sip.*; +import javax.sip.address.*; +import javax.sip.header.*; +import javax.sip.message.*; +import java.net.*; +import java.net.URI; +import java.text.*; +import java.util.*; /** * A SIP implementation of the Protocol Provider Service. @@ -33,6 +32,7 @@ import gov.nist.javax.sip.message.*; * @author Emil Ivov * @author Lubomir Marinov * @author Alan Kelly + * @author Grigorii Balutsel */ public class ProtocolProviderServiceSipImpl extends AbstractProtocolProviderService @@ -132,6 +132,41 @@ public class ProtocolProviderServiceSipImpl public static final String KEEP_ALIVE_INTERVAL = "KEEP_ALIVE_INTERVAL"; /** + * The name of the property under which the user may specify whether to use + * or not XCAP. + */ + public static final String XCAP_ENABLE = "XCAP_ENABLE"; + + /** + * The name of the property under which the user may specify whether to use + * original sip creadetials for the XCAP. + */ + public static final String XCAP_USE_SIP_CREDETIALS = + "XCAP_USE_SIP_CREDETIALS"; + + /** + * The name of the property under which the user may specify the XCAP server + * uri. + */ + public static final String XCAP_SERVER_URI = "XCAP_SERVER_URI"; + + /** + * The name of the property under which the user may specify the XCAP user. + */ + public static final String XCAP_USER = "XCAP_USER"; + + /** + * The name of the property under which the user may specify the XCAP user + * password. + */ + public static final String XCAP_PASSWORD = "XCAP_PASSWORD"; + + /** + * Presence content for image. + */ + public static final String PRES_CONTENT_IMAGE_NAME = "sip_communicator"; + + /** * The default maxForwards header that we use in our requests. */ private MaxForwardsHeader maxForwardsHeader = null; @@ -186,6 +221,21 @@ public class ProtocolProviderServiceSipImpl private SipStatusEnum sipStatusEnum; /** + * The XCAP client. + */ + private final XCapClient xCapClient = new XCapClientImpl(); + + /** + * Gets the XCAP client. + * + * @return the XCAP client. + */ + public XCapClient getXCapClient() + { + return xCapClient; + } + + /** * 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. @@ -251,6 +301,64 @@ public class ProtocolProviderServiceSipImpl return this.registeredEvents; } + + public void fireRegistrationStateChanged(RegistrationState oldState, + RegistrationState newState, + int reasonCode, + String reason) + { + if (newState.equals(RegistrationState.REGISTERED)) + { + try + { + boolean enableXCap = + accountID.getAccountPropertyBoolean(XCAP_ENABLE, true); + boolean useSipCredetials = + accountID.getAccountPropertyBoolean( + XCAP_USE_SIP_CREDETIALS, true); + String serverUri = + accountID.getAccountPropertyString(XCAP_SERVER_URI); + String user; + String password; + if (useSipCredetials) + { + user = accountID.getAccountPropertyString( + ProtocolProviderFactory.USER_ID); + password = SipActivator.getProtocolProviderFactory(). + loadPassword(accountID); + } + else + { + user = accountID.getAccountPropertyString(XCAP_USER); + password = accountID.getAccountPropertyString(XCAP_PASSWORD); + } + // Connect to xcap server + Address userAddress = parseAddressString(user); + if(enableXCap && serverUri != null) + { + URI uri = new URI(serverUri.trim()); + if(uri.getHost() != null && uri.getPath() != null) + { + xCapClient.connect(uri, userAddress, password); + } + } + } + catch (Exception e) + { + logger.error("Error while connecting to XCAP server. " + + "Contact list won't be saved", e); + } + } + else if (newState.equals(RegistrationState.UNREGISTERING) || + newState.equals(RegistrationState.CONNECTION_FAILED)) + { + xCapClient.dicsonnect(); + } + + super.fireRegistrationStateChanged(oldState, newState, reasonCode, + reason); + } + /** * Starts the registration process. Connection details such as * registration server, user name/number are provided through the @@ -480,6 +588,11 @@ public class ProtocolProviderServiceSipImpl new OperationSetTypingNotificationsSipImpl( this, opSetBasicIM)); + // init avatar + addSupportedOperationSet( + OperationSetServerStoredAccountInfo.class, + new OperationSetServerStoredAccountInfoSipImpl(this)); + } // OperationSetVideoTelephony diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ServerStoredContactListSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ServerStoredContactListSipImpl.java new file mode 100644 index 0000000..6ee87cc --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/ServerStoredContactListSipImpl.java @@ -0,0 +1,1234 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip; + +import net.java.sip.communicator.impl.protocol.sip.xcap.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.presrules.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +import javax.sip.address.*; +import java.text.*; +import java.util.*; + +/** + * Encapsulates XCapClient, it's resposible for generate corresponding + * sip-communicator events to all action that are made with XCAP contacts and + * groups. + * + * @author Grigorii Balutsel + */ +public class ServerStoredContactListSipImpl +{ + /** + * Logger class + */ + private static final Logger logger = + Logger.getLogger(ServerStoredContactListSipImpl.class); + + /** + * Root group name. + */ + private static String ROOT_GROUP_NAME = "RootGroup"; + + /** + * "White" rule identifier. + */ + private static String WHITE_RULE_ID = "sip_communicator"; + + /** + * The provider that is on top of us. + */ + private final ProtocolProviderServiceSipImpl sipProvider; + + /** + * The operation set that created us and that we could use when dispatching + * subscription events. + */ + private final AbstractOperationSetPersistentPresence<ProtocolProviderServiceSipImpl> + parentOperationSet; + + /** + * Listeners that would receive event notifications for changes in group + * names or other properties, removal or creation of groups. + */ + private final Vector<ServerStoredGroupListener> serverStoredGroupListeners; + + /** + * The root contact group. The container for all SIP contacts and groups. + */ + private final ContactGroupSipImpl rootGroup; + + /** + * Current presence rules. + */ + private RulesetType presRules; + + /** + * Current "white" rule. + */ + private RuleType whiteRule; + + /** + * Creates a ServerStoredContactList wrapper for the specified BuddyList. + * + * @param sipProvider the provider that has instantiated us. + * @param parentOperationSet the operation set that created us and that + * we could use for dispatching subscription events + */ + ServerStoredContactListSipImpl( + ProtocolProviderServiceSipImpl sipProvider, + AbstractOperationSetPersistentPresence<ProtocolProviderServiceSipImpl> + parentOperationSet) + { + this.sipProvider = sipProvider; + this.parentOperationSet = parentOperationSet; + this.serverStoredGroupListeners = + new Vector<ServerStoredGroupListener>(); + this.rootGroup = new ContactGroupSipImpl(ROOT_GROUP_NAME, sipProvider); + } + + /** + * Returns the root group of the contact list. + * + * @return the root ContactGroup for the ContactList. + */ + public ContactGroupSipImpl 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. + */ + public void addGroupListener(ServerStoredGroupListener listener) + { + synchronized (serverStoredGroupListeners) + { + if (!serverStoredGroupListeners.contains(listener)) + { + this.serverStoredGroupListeners.add(listener); + } + } + } + + /** + * Removes the specified group listener so that it won't receive further + * events on group modification/creation/destruction. + * + * @param listener the ServerStoredGroupListener to unregister. + */ + public 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. + */ + void fireGroupEvent(ContactGroup group, int eventID) + { + ServerStoredGroupEvent event = new ServerStoredGroupEvent( + group, + eventID, + parentOperationSet.getServerStoredContactListRoot(), + sipProvider, + parentOperationSet); + if (logger.isTraceEnabled()) + { + logger.trace("Will dispatch the following group event: " + event); + } + Iterable<ServerStoredGroupListener> listeners; + synchronized (serverStoredGroupListeners) + { + listeners = + new ArrayList<ServerStoredGroupListener>( + serverStoredGroupListeners); + } + for (ServerStoredGroupListener listener : listeners) + { + if (eventID == ServerStoredGroupEvent.GROUP_REMOVED_EVENT) + { + listener.groupRemoved(event); + } + else if (eventID == ServerStoredGroupEvent.GROUP_RENAMED_EVENT) + { + listener.groupNameChanged(event); + } + else if (eventID == ServerStoredGroupEvent.GROUP_CREATED_EVENT) + { + listener.groupCreated(event); + } + else if (eventID == ServerStoredGroupEvent.GROUP_RESOLVED_EVENT) + { + listener.groupResolved(event); + } + } + } + + /** + * 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. If creation is successfull event will be fired. + * + * @param parentGroup the group where the unersolved contact is to be + * created. + * @param contactId the sip id of the contact to create. + * @return the newly created unresolved <tt>ContactSipImpl</tt>. + */ + public synchronized ContactSipImpl createUnresolvedContact( + ContactGroupSipImpl parentGroup, String contactId) + { + if (parentGroup == null) + { + throw new IllegalArgumentException("Parent group cannot be null"); + } + if (contactId == null || contactId.length() == 0) + { + throw new IllegalArgumentException( + "Creating contact id name cannot be null or empty"); + } + Address contactAddress; + try + { + contactAddress = sipProvider.parseAddressString(contactId); + } + catch (ParseException ex) + { + throw new IllegalArgumentException( + String.format("%1s is no a valid SIP identifier", + contactId), + ex); + } + logger.trace("createUnresolvedContact " + contactId); + ContactSipImpl newUnresolvedContact = new ContactSipImpl(contactAddress, + sipProvider); + parentGroup.addContact(newUnresolvedContact); + fireContactAdded(parentGroup, newUnresolvedContact); + return newUnresolvedContact; + } + + /** + * Creates contact for the specified address and inside the + * specified group . If creation is successfull event will be fired. + * + * @param parentGroup the group where the unersolved contact is to be + * created. + * @param contactId the sip id of the contact to create. + * @param persistent specify whether created contact is persistent ot not. + * @return the newly created <tt>ContactSipImpl</tt>. + * @throws OperationFailedException with code NETWORK_FAILURE if the + * operation if failed during network + * communication. + */ + synchronized public ContactSipImpl createContact( + ContactGroupSipImpl parentGroup, String contactId, + boolean persistent) + throws OperationFailedException + { + if (parentGroup == null) + { + throw new IllegalArgumentException("Parent group cannot be null"); + } + if (contactId == null || contactId.trim().length() == 0) + { + throw new IllegalArgumentException( + "Contact identifier cannot be null or empty"); + } + if (logger.isTraceEnabled()) + { + logger.trace( + String.format("createContact %1s, %2s, %3s", + parentGroup.getGroupName(), contactId, persistent)); + } + if (parentGroup.getContact(contactId) != null) + { + throw new OperationFailedException( + "Contact " + contactId + " already exists.", + OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS); + } + Address contactAddress; + try + { + contactAddress = sipProvider.parseAddressString(contactId); + } + catch (ParseException ex) + { + throw new IllegalArgumentException(contactId + + " is not a valid string.", ex); + } + + ContactSipImpl newContact = new ContactSipImpl(contactAddress, + sipProvider); + newContact.setPersistent(persistent); + String name = ((SipURI) contactAddress.getURI()).getUser(); + newContact.setDisplayName(name); + parentGroup.addContact(newContact); + if (newContact.isPersistent()) + { + // Update resoure-lists + try + { + updateResourceLists(); + } + catch (XCapException e) + { + parentGroup.removeContact(newContact); + throw new OperationFailedException( + "Error while creating XCAP contact", + OperationFailedException.NETWORK_FAILURE, e); + } + // Update pres-rules if needed + if (!isContactExistsInWhiteRule(contactId)) + { + // Update pres-rules + addContactToWhiteList(newContact); + try + { + updatePresRules(); + } + catch (XCapException e) + { + logger.error("Error while creating XCAP contact", e); + } + } + } + fireContactAdded(parentGroup, newContact); + return newContact; + } + + /** + * Removes a contact. If creation is successfull event will be fired. + * + * @param contact contact to be removed. + * @throws OperationFailedException with code NETWORK_FAILURE if the + * operation if failed during network + * communication. + */ + synchronized public void removeContact(ContactSipImpl contact) + throws OperationFailedException + { + if (contact == null) + { + throw new IllegalArgumentException( + "Removing contact cannot be null"); + } + logger.trace("removeContact " + contact.getUri()); + ContactGroupSipImpl parentGroup = + (ContactGroupSipImpl) contact.getParentContactGroup(); + parentGroup.removeContact(contact); + if (contact.isPersistent()) + { + // Update resoure-lists + try + { + updateResourceLists(); + } + catch (XCapException e) + { + parentGroup.removeContact(contact); + throw new OperationFailedException( + "Error while removing XCAP contact", + OperationFailedException.NETWORK_FAILURE, e); + } + // Update pres-rules if contact doesn't exist + if (!isContactPersistent(contact.getUri())) + { + removeContactFromWhiteList(contact); + try + { + updatePresRules(); + } + catch (XCapException e) + { + logger.error("Error while removing XCAP contact", e); + } + } + } + fireContactRemoved(parentGroup, contact); + } + + /** + * Removes the specified contact from its current parent and places it + * under <tt>newParent</tt>. + * + * @param contact the <tt>Contact</tt> to move + * @param newParentGroup the <tt>ContactGroup</tt> where <tt>Contact</tt> + * would be placed. + * @throws OperationFailedException with code NETWORK_FAILURE if the + * operation if failed during network + * communication. + */ + public void moveContactToGroup( + ContactSipImpl contact, + ContactGroupSipImpl newParentGroup) + throws OperationFailedException + { + if (contact == null) + { + throw new IllegalArgumentException( + "Moving contact cannot be null"); + } + if (newParentGroup == null) + { + throw new IllegalArgumentException( + "New contact's parent group be null"); + } + if (newParentGroup.getContact(contact.getUri()) != null) + { + throw new OperationFailedException( + "Contact " + contact.getUri() + " already exists.", + OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS); + } + ContactGroupSipImpl oldParentGroup = + (ContactGroupSipImpl) contact.getParentContactGroup(); + oldParentGroup.removeContact(contact); + newParentGroup.addContact(contact); + if (contact.isPersistent()) + { + try + { + updateResourceLists(); + } + catch (XCapException e) + { + newParentGroup.removeContact(contact); + oldParentGroup.addContact(contact); + throw new OperationFailedException( + "Error while moving XCAP contact", + OperationFailedException.NETWORK_FAILURE, e); + } + } + fireContactMoved(oldParentGroup, newParentGroup, contact); + } + + /** + * Renames the specified contac. + * + * @param contact the contact to be renameed. + * @param newName the new contact name. + * @throws OperationFailedException with code NETWORK_FAILURE if the + * operation if failed during network + * communication. + */ + synchronized public void renameContact( + ContactSipImpl contact, + String newName) + throws OperationFailedException + { + if (contact == null) + { + throw new IllegalArgumentException( + "Renaming contact cannot be null"); + } + String oldName = contact.getDisplayName(); + if (oldName.equals(newName)) + { + return; + } + contact.setDisplayName(newName); + if (contact.isPersistent()) + { + try + { + updateResourceLists(); + } + catch (XCapException e) + { + contact.setDisplayName(oldName); + throw new OperationFailedException( + "Error while renaming XCAP group", + OperationFailedException.NETWORK_FAILURE, e); + } + } + parentOperationSet.fireContactPropertyChangeEvent( + ContactPropertyChangeEvent.PROPERTY_DISPLAY_NAME, + contact, + oldName, + newName); + } + + + /** + * 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 parentGroup the group under which the new group is to be created. + * @param groupName the name of the group to create. + * @return the newly created unresolved <tt>ContactGroupSipImpl</tt>. + */ + synchronized public ContactGroupSipImpl createUnresolvedContactGroup( + ContactGroupSipImpl parentGroup, + String groupName) + { + if (parentGroup == null) + { + throw new IllegalArgumentException("Parent group cannot be null"); + } + if (groupName == null || groupName.length() == 0) + { + throw new IllegalArgumentException( + "Creating group name cannot be null or empry"); + } + if (logger.isTraceEnabled()) + { + logger.trace("createUnresolvedContactGroup " + groupName); + } + ContactGroupSipImpl subGroup = new ContactGroupSipImpl(groupName, + sipProvider); + subGroup.setResolved(false); + parentGroup.addSubgroup(subGroup); + fireGroupEvent(subGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT); + return subGroup; + } + + /** + * Creates a group with the specified name and parent in the server stored + * contact list. + * + * @param parentGroup the group where the new group should be created. + * @param groupName the name of the new group to create. + * @param persistent specify whether created contact is persistent ot not. + * @return the newly created <tt>ContactGroupSipImpl</tt>. + * @throws OperationFailedException with code NETWORK_FAILURE if creating + * the group fails because of XCAP server + * error or with code + * CONTACT_GROUP_ALREADY_EXISTS if contact + * group with such name already exists. + */ + synchronized public ContactGroupSipImpl createGroup( + ContactGroupSipImpl parentGroup, String groupName, + boolean persistent) + throws OperationFailedException + { + if (parentGroup == null) + { + throw new IllegalArgumentException("Parent group cannot be null"); + } + if (groupName == null || groupName.length() == 0) + { + throw new IllegalArgumentException( + "Creating group name cannot be null or empry"); + } + if (logger.isTraceEnabled()) + { + logger.trace("createGroup " + parentGroup.getGroupName() + "," + + groupName + "," + persistent); + } + if (parentGroup.getGroup(groupName) != null) + { + throw new OperationFailedException( + String.format("Group %1s already exists.", groupName), + OperationFailedException.CONTACT_GROUP_ALREADY_EXISTS); + } + ContactGroupSipImpl subGroup = + new ContactGroupSipImpl(groupName, sipProvider); + subGroup.setPersistent(persistent); + parentGroup.addSubgroup(subGroup); + if (subGroup.isPersistent()) + { + try + { + updateResourceLists(); + } + catch (XCapException e) + { + parentGroup.removeSubGroup(subGroup); + throw new OperationFailedException( + "Error while creating XCAP group", + OperationFailedException.NETWORK_FAILURE, e); + } + } + fireGroupEvent(subGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT); + return subGroup; + } + + /** + * Removes the specified group from the server stored contact list. + * + * @param group the group to delete. + */ + synchronized public void removeGroup(ContactGroupSipImpl group) + { + if (group == null) + { + throw new IllegalArgumentException("Removing group cannot be null"); + } + if (rootGroup.equals(group)) + { + throw new IllegalArgumentException("Root group cannot be deleted"); + } + if (logger.isTraceEnabled()) + { + logger.trace("removeGroup " + group.getGroupName()); + } + ContactGroupSipImpl parentGroup = + (ContactGroupSipImpl) group.getParentContactGroup(); + parentGroup.removeSubGroup(group); + if (group.isPersistent()) + { + try + { + updateResourceLists(); + } + catch (XCapException e) + { + parentGroup.addSubgroup(group); + throw new IllegalStateException( + "Error while removing XCAP group", e); + } + } + fireGroupEvent(group, 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. + */ + synchronized public void renameGroup( + ContactGroupSipImpl group, + String newName) + { + if (group == null) + { + throw new IllegalArgumentException("Renaming group cannot be null"); + } + if (rootGroup.equals(group)) + { + throw new IllegalArgumentException("Root group cannot be renamed"); + } + String oldName = group.getGroupName(); + if (oldName.equals(newName)) + { + return; + } + ContactGroupSipImpl parentGroup = + (ContactGroupSipImpl) group.getParentContactGroup(); + if (parentGroup.getGroup(newName) != null) + { + throw new IllegalStateException( + String.format("Group with name %1s already exists", + newName)); + } + group.setName(newName); + if (group.isPersistent()) + { + try + { + updateResourceLists(); + } + catch (XCapException e) + { + group.setName(oldName); + throw new IllegalStateException( + "Error while renaming XCAP group", e); + } + } + fireGroupEvent(group, ServerStoredGroupEvent.GROUP_RENAMED_EVENT); + } + + /** + * 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. + */ + private void fireContactAdded( + ContactGroupSipImpl parentGroup, + ContactSipImpl contact) + { + parentOperationSet.fireSubscriptionEvent( + contact, + parentGroup, + SubscriptionEvent.SUBSCRIPTION_CREATED); + } + + /** + * 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( + ContactGroupSipImpl oldParentGroup, + ContactGroupSipImpl newParentGroup, + ContactSipImpl contact) + { + parentOperationSet.fireSubscriptionMovedEvent( + contact, + oldParentGroup, + newParentGroup); + } + + /** + * 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( + ContactGroupSipImpl parentGroup, + ContactSipImpl contact) + { + parentOperationSet.fireSubscriptionEvent( + contact, + parentGroup, + SubscriptionEvent.SUBSCRIPTION_REMOVED); + } + + /** + * 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. + */ + private void fireContactResolved( + ContactGroupSipImpl parentGroup, + ContactSipImpl contact) + { + parentOperationSet.fireSubscriptionEvent( + contact, + parentGroup, + SubscriptionEvent.SUBSCRIPTION_RESOLVED); + } + + /** + * Initializes the server stored list. Synchronize server stored groups and + * contacts with the local groups and contacts. + */ + synchronized public void init() + { + try + { + XCapClient xCapClient = sipProvider.getXCapClient(); + if (!xCapClient.isConnected() || + !xCapClient.isResourceListsSupported()) + { + return; + } + ResourceListsType resourceLists = xCapClient.getResourceLists(); + // Collect all root's subgroups to check if some of them were deleted + ListType serverRootList = new ListType(); + //serverRootList.getLists().addAll(resourceLists.getList()); + for (ListType list : resourceLists.getList()) + { + // If root group has sub group with ROOT_GROUP_NAME - it is + // special group for storing contacts that is not allowed by RFC + if (list.getName().equals(ROOT_GROUP_NAME)) + { + serverRootList.setName(ROOT_GROUP_NAME); + serverRootList.setDisplayName(list.getDisplayName()); + serverRootList.getEntries().addAll(list.getEntries()); + serverRootList.getEntryRefs().addAll(list.getEntryRefs()); + serverRootList.getExternals().addAll(list.getExternals()); + serverRootList.setAny(list.getAny()); + serverRootList + .setAnyAttributes(list.getAnyAttributes()); + } + else + { + serverRootList.getLists().add(list); + } + } + resolveContactGroup(rootGroup, serverRootList); + if (xCapClient.isPresRulesSupported()) + { + // Get pres-rules and analyze it + presRules = xCapClient.getPresRules(); + for (RuleType rule : presRules.getRule()) + { + if (rule.getId().equals(WHITE_RULE_ID)) + { + whiteRule = rule; + break; + } + } + // If "white" rule is available refresh it + if (whiteRule != null) + { + presRules.getRule().remove(whiteRule); + } + whiteRule = createWhiteRule(); + presRules.getRule().add(whiteRule); + // Add contacts into the "white" rule + List<ContactSipImpl> uniqueContacts = + getUniqueContacts(rootGroup); + for (ContactSipImpl contact : uniqueContacts) + { + addContactToWhiteList(contact); + } + updatePresRules(); + } + } + catch (XCapException e) + { + logger.error(e); + } + } + + /** + * Destroys the server stored list. + */ + synchronized public void destroy() + { + List<ContactSipImpl> contacts = getAllContacts(rootGroup); + for (ContactSipImpl contact : contacts) + { + contact.setResolvable(false); + } + presRules = null; + whiteRule = null; + } + + /** + * Creates "white" rule with full permissions. + * + * @return created rule. + */ + private static RuleType createWhiteRule() + { + RuleType whiteList = new RuleType(); + whiteList.setId(WHITE_RULE_ID); + + ConditionsType conditions = new ConditionsType(); + whiteList.setConditions(conditions); + + ActionsType actions = new ActionsType(); + actions.setSubHandling(SubHandlingType.Allow); + whiteList.setActions(actions); + + TransfomationsType transfomations = new TransfomationsType(); + ProvideServicePermission servicePermission = + new ProvideServicePermission(); + servicePermission.setAllServices( + new ProvideServicePermission.AllServices()); + transfomations.setServicePermission(servicePermission); + ProvidePersonPermission personPermission = + new ProvidePersonPermission(); + personPermission.setAllPersons( + new ProvidePersonPermission.AllPersons()); + transfomations.setPersonPermission(personPermission); + ProvideDevicePermission devicePermission = + new ProvideDevicePermission(); + devicePermission.setAllDevices( + new ProvideDevicePermission.AllDevices()); + transfomations.setDevicePermission(devicePermission); + whiteList.setTransformations(transfomations); + + return whiteList; + } + + /** + * Adds contact to the "white" rule. + * + * @param contact the contact to add. + */ + private void addContactToWhiteList(ContactSipImpl contact) + { + XCapClient xCapClient = sipProvider.getXCapClient(); + if (!xCapClient.isConnected() || !xCapClient.isPresRulesSupported()) + { + return; + } + IdentityType identity; + if (whiteRule.getConditions().getIdentityOrSphereOrValidity(). + size() == 0) + { + identity = new IdentityType(); + whiteRule.getConditions().getIdentityOrSphereOrValidity() + .add(identity); + } + else + { + identity = (IdentityType) whiteRule.getConditions() + .getIdentityOrSphereOrValidity().get(0); + } + OneType one = new OneType(); + one.setId(contact.getUri()); + identity.getOneList().add(one); + } + + /** + * Indicates whether or not contact is exists in the "white" rule. + * + * @param contactUri the contact uri. + * @return true if contact is exists, false if not. + */ + private boolean isContactExistsInWhiteRule(String contactUri) + { + XCapClient xCapClient = sipProvider.getXCapClient(); + if (!xCapClient.isConnected() || !xCapClient.isPresRulesSupported()) + { + return false; + } + IdentityType identity; + if (whiteRule.getConditions().getIdentityOrSphereOrValidity(). + size() == 0) + { + return false; + } + identity = (IdentityType) whiteRule.getConditions() + .getIdentityOrSphereOrValidity().get(0); + for (OneType one : identity.getOneList()) + { + if (one.getId().equals(contactUri)) + { + return true; + } + } + return false; + } + + /** + * Removes contact from the "white" rule. + * + * @param contact the contact to remove. + */ + private void removeContactFromWhiteList(ContactSipImpl contact) + { + XCapClient xCapClient = sipProvider.getXCapClient(); + if (!xCapClient.isConnected() || !xCapClient.isPresRulesSupported()) + { + return; + } + IdentityType identity = (IdentityType) + whiteRule.getConditions().getIdentityOrSphereOrValidity(). + get(0); + OneType contactOne = null; + for (OneType one : identity.getOneList()) + { + if (contact.getUri().equals(one.getId())) + { + contactOne = one; + break; + } + } + if (contactOne != null) + { + identity.getOneList().remove(contactOne); + } + if (identity.getOneList().size() == 0) + { + whiteRule.getConditions().getIdentityOrSphereOrValidity(). + remove(identity); + } + } + + /** + * Returns all avaliable contacts from group and all subgroups. + * + * @param group the parent of the contacts. + * @return list of availcable contacts. + */ + public synchronized List<ContactSipImpl> getAllContacts( + ContactGroupSipImpl group) + { + List<ContactSipImpl> contacts = new ArrayList<ContactSipImpl>(); + Iterator<ContactGroup> groupIterator = group.subgroups(); + while (groupIterator.hasNext()) + { + contacts.addAll( + getAllContacts((ContactGroupSipImpl) groupIterator.next())); + } + Iterator<Contact> contactIterator = group.contacts(); + while (contactIterator.hasNext()) + { + ContactSipImpl contact = (ContactSipImpl) contactIterator.next(); + contacts.add(contact); + } + return contacts; + } + + /** + * Gets all unique contacts from group and all subgroups. + * + * @param group the parent of the contacts. + * @return List of available contacts + */ + public synchronized List<ContactSipImpl> getUniqueContacts( + ContactGroupSipImpl group) + { + Map<String, ContactSipImpl> uniqueContacts = + new HashMap<String, ContactSipImpl>(); + List<ContactSipImpl> contacts = getAllContacts(group); + for (ContactSipImpl contact : contacts) + { + uniqueContacts.put(contact.getUri(), contact); + } + return new ArrayList<ContactSipImpl>(uniqueContacts.values()); + } + + /** + * Indicates whether or not contact is exists. + * + * @param contactUri the contact uri. + * @return true if contact is exists, false if not. + */ + private boolean isContactExists(String contactUri) + { + for (ContactSipImpl uniqueContact : getUniqueContacts(rootGroup)) + { + if (uniqueContact.getUri().equals(contactUri)) + { + return true; + } + } + return false; + } + + /** + * Gets all contacts with the specified uri. + * + * @param contactUri the contact uri. + * @return the list of the contacts. + */ + private List<ContactSipImpl> getContacts(String contactUri) + { + List<ContactSipImpl> result = new ArrayList<ContactSipImpl>(); + for (ContactSipImpl contact : getAllContacts(rootGroup)) + { + if (contact.getUri().equals(contactUri)) + { + result.add(contact); + } + } + return result; + } + + /** + * Indicates whether or not contact is exists. + * + * @param contactUri contactUri the contact uri. + * @return true if at least one contact is persistent, false if not. + */ + private boolean isContactPersistent(String contactUri) + { + for (ContactSipImpl contact : getContacts(contactUri)) + { + if (contact.isPersistent()) + { + return true; + } + } + return false; + } + + /** + * Resolves local group with server stored group. + * <p/> + * If local group exsists GROUP_CREATED_RESOLVED will be fired. + * <p/> + * If local group doesn't exsist GROUP_CREATED_EVENT will be fired. + * <p/> + * If server group doesn't represented GROUP_REMOVED_EVENT will be fired. + * + * @param clientGroup the local group. + * @param serverGroup the server stored group. + */ + private void resolveContactGroup( + ContactGroupSipImpl clientGroup, + ListType serverGroup) + { + // Gather client information + List<ContactGroupSipImpl> unresolvedGroups = + new ArrayList<ContactGroupSipImpl>(); + Iterator<ContactGroup> groupIterator = clientGroup.subgroups(); + while (groupIterator.hasNext()) + { + ContactGroupSipImpl group = + (ContactGroupSipImpl) groupIterator.next(); + unresolvedGroups.add(group); + } + List<ContactSipImpl> unresolvedContacts = + new ArrayList<ContactSipImpl>(); + Iterator<Contact> contactIterator = clientGroup.contacts(); + while (contactIterator.hasNext()) + { + ContactSipImpl contact = (ContactSipImpl) contactIterator.next(); + unresolvedContacts.add(contact); + } + // Process all server groups and fire events + for (ListType serverList : serverGroup.getLists()) + { + ContactGroupSipImpl newGroup = + (ContactGroupSipImpl) clientGroup.getGroup( + serverList.getName()); + if (newGroup == null) + { + newGroup = new ContactGroupSipImpl(serverList.getName(), + sipProvider); + newGroup.setOtherAttributes(serverList.getAnyAttributes()); + newGroup.setAny(serverList.getAny()); + newGroup.setResolved(true); + clientGroup.addSubgroup(newGroup); + // Tell listeners about the added group + + fireGroupEvent(newGroup, + ServerStoredGroupEvent.GROUP_CREATED_EVENT); + resolveContactGroup(newGroup, serverList); + } + else + { + newGroup.setResolved(true); + newGroup.setOtherAttributes(serverList.getAnyAttributes()); + newGroup.setAny(serverList.getAny()); + unresolvedGroups.remove(newGroup); + // Tell listeners about the resolved group + + fireGroupEvent(newGroup, + ServerStoredGroupEvent.GROUP_RESOLVED_EVENT); + resolveContactGroup(newGroup, serverList); + } + } + // Process all server contacts and fire events + for (EntryType serverEntry : serverGroup.getEntries()) + { + ContactSipImpl newContact = + (ContactSipImpl) clientGroup.getContact( + serverEntry.getUri()); + if (newContact == null) + { + Address sipAddress; + try + { + sipAddress = + sipProvider.parseAddressString( + serverEntry.getUri()); + } + catch (ParseException e) + { + logger.error(e); + continue; + } + newContact = new ContactSipImpl(sipAddress, sipProvider); + newContact.setDisplayName(serverEntry.getDisplayName()); + newContact.setOtherAttributes(serverEntry.getAnyAttributes()); + newContact.setAny(serverEntry.getAny()); + newContact.setResolved(true); + clientGroup.addContact(newContact); + + fireContactAdded(clientGroup, newContact); + } + else + { + newContact.setDisplayName(serverEntry.getDisplayName()); + newContact.setOtherAttributes(serverEntry.getAnyAttributes()); + newContact.setAny(serverEntry.getAny()); + newContact.setResolved(true); + unresolvedContacts.remove(newContact); + + fireContactResolved(clientGroup, newContact); + } + } + // Save all others + // TODO: process externals and enrty-refs after sip2sip fixes + clientGroup.getList().getExternals().addAll(serverGroup.getExternals()); + clientGroup.getList().getEntryRefs().addAll(serverGroup.getEntryRefs()); + clientGroup.getList().getAny().addAll(serverGroup.getAny()); + + // Remove unresolved contacts + for (ContactSipImpl unresolvedContact : unresolvedContacts) + { + unresolvedContact.setResolved(true); + clientGroup.removeContact(unresolvedContact); + // Tell listeners about the removed contact + + fireContactRemoved(clientGroup, unresolvedContact); + } + // Remove unresolved groups + for (ContactGroupSipImpl unresolvedGroup : unresolvedGroups) + { + unresolvedGroup.setResolved(true); + clientGroup.removeSubGroup(unresolvedGroup); + // Tell listeners about the removed group + + fireGroupEvent(unresolvedGroup, + ServerStoredGroupEvent.GROUP_REMOVED_EVENT); + } + } + + /** + * Puts resource-lists to the server. + * + * @throws XCapException if there is some error during operation. + */ + synchronized private void updateResourceLists() + throws XCapException + { + XCapClient xCapClient = sipProvider.getXCapClient(); + if (!xCapClient.isConnected() || !xCapClient.isResourceListsSupported()) + { + return; + } + ResourceListsType resourceLists = new ResourceListsType(); + for (ListType list : rootGroup.getList().getLists()) + { + resourceLists.getList().add(list); + } + // Create special root group + ListType serverRootList = new ListType(); + serverRootList.setName(ROOT_GROUP_NAME); + serverRootList.setDisplayName(rootGroup.getList().getDisplayName()); + serverRootList.getEntries().addAll(rootGroup.getList().getEntries()); + serverRootList.getEntryRefs() + .addAll(rootGroup.getList().getEntryRefs()); + serverRootList.getExternals() + .addAll(rootGroup.getList().getExternals()); + serverRootList.setAny(rootGroup.getList().getAny()); + serverRootList + .setAnyAttributes(rootGroup.getList().getAnyAttributes()); + resourceLists.getList().add(serverRootList); + + xCapClient.putResourceLists(resourceLists); + } + + /** + * Puts pres-rules to the server. + * + * @throws XCapException if there is some error during operation. + */ + synchronized private void updatePresRules() + throws XCapException + { + XCapClient xCapClient = sipProvider.getXCapClient(); + if (!xCapClient.isConnected() || !xCapClient.isPresRulesSupported()) + { + return; + } + xCapClient.putPresRules(presRules); + } +}
\ No newline at end of file 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 30827e4..55a9d2d 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,6 +5,32 @@ Bundle-Vendor: sip-communicator.org Bundle-Version: 0.0.1 System-Bundle: yes Import-Package: org.apache.log4j, + org.apache.http, + org.apache.http.entity, + org.apache.http.impl, + org.apache.http.impl.entity, + org.apache.http.impl.io, + org.apache.http.io, + org.apache.http.message, + org.apache.http.params, + org.apache.http.protocol, + org.apache.http.util, + org.apache.http.annotation, + org.apache.http.auth, + org.apache.http.auth.params, + org.apache.http.client, + org.apache.http.client.entity, + org.apache.http.client.methods, + org.apache.http.client.params, + org.apache.http.client.protocol, + org.apache.http.client.utils, + org.apache.http.conn, + org.apache.http.conn.params, + org.apache.http.conn.routing, + org.apache.http.conn.scheme, + org.apache.http.conn.ssl, + org.apache.http.conn.util, + org.apache.http.impl.client, org.osgi.framework, org.w3c.dom, org.xml.sax, @@ -25,7 +51,20 @@ Import-Package: org.apache.log4j, net.java.sip.communicator.util, net.java.sip.communicator.util.xml, javax.net.ssl, + javax.security.auth.x500, + javax.xml.datatype, javax.xml.parsers, + javax.xml.namespace, javax.xml.transform, javax.xml.transform.dom, - javax.xml.transform.stream + javax.xml.transform.stream, +Export-Package: net.java.sip.communicator.impl.protocol.sip, + net.java.sip.communicator.impl.protocol.sip.xcap, + net.java.sip.communicator.impl.protocol.sip.xcap.utils, + net.java.sip.communicator.impl.protocol.sip.xcap.model, + net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps, + net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists, + net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent, + net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror + + diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/BaseHttpXCapClient.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/BaseHttpXCapClient.java new file mode 100644 index 0000000..4467c29 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/BaseHttpXCapClient.java @@ -0,0 +1,483 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +import net.java.sip.communicator.impl.protocol.sip.xcap.utils.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror.*; +import net.java.sip.communicator.util.*; +import org.apache.http.*; +import org.apache.http.auth.*; +import org.apache.http.client.methods.*; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.*; + +import javax.sip.address.*; +import java.io.*; +import java.net.URI; +import java.net.*; + +/** + * Base HTTP XCAP client implementation. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public abstract class BaseHttpXCapClient implements HttpXCapClient +{ + /** + * Class logger. + */ + private static final Logger logger = + Logger.getLogger(BaseHttpXCapClient.class); + + /** + * HTTP Content-Type header. + */ + public static final String HEADER_CONTENT_TYPE = "Content-Type"; + + /** + * HTTP ETag header. + */ + public static final String HEADER_ETAG = "ETag"; + + /** + * HTTP If-None-Match header. + */ + public static final String HEADER_IF_NONE_MATCH = "If-None-Match"; + + /** + * XCap-error content type. + */ + public static final String XCAP_ERROR_CONTENT_TYPE + = "application/xcap-error+xml"; + + /** + * The default timeout (10 seconds) + */ + private static int DEFAULT_TIMEOUT = 10 * 1000; + + /** + * Current server uri. + */ + protected URI uri; + + /** + * Current user. + */ + protected Address userAddress; + + /** + * Current user password. + */ + private String password; + + /** + * Indicates whether or not client is connected. + */ + private boolean connected; + + /** + * How many seconds should the client wait for HTTP response. + */ + private int timeout; + + /** + * Creates an instance of this XCAP client. + */ + public BaseHttpXCapClient() + { + timeout = DEFAULT_TIMEOUT; + } + + /** + * Connects user to XCap server. + * + * @param uri the server location. + * @param userAddress the user name. + * @param password the user password. + * @throws XCapException if there is some error during operation. + */ + public void connect(URI uri, Address userAddress, String password) + throws XCapException + { + if (!userAddress.getURI().isSipURI()) + { + throw new IllegalArgumentException("Address must contains SipUri"); + } + this.uri = uri; + this.userAddress = (Address) userAddress.clone(); + this.password = password == null ? "" : password; + connected = true; + } + + /** + * Checks if user is connected to the XCAP server. + * + * @return true if user is connected. + */ + public boolean isConnected() + { + return connected; + } + + /** + * Dicsonnects user from the XCAP server. + */ + public void dicsonnect() + { + this.uri = null; + this.userAddress = null; + this.password = null; + connected = false; + } + + /** + * Gets the resource from the server. + * + * @param resourceId resource identifier. + * @return the server response. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public XCapHttpResponse get(XCapResourceId resourceId) + throws XCapException + { + return get(getResourceURI(resourceId)); + } + + /** + * Gets resource from the server. + * + * @param uri the resource uri. + * @return the server response. + * @throws XCapException if there is error during reading the resource's + * content. + */ + protected XCapHttpResponse get(URI uri) + throws XCapException + { + DefaultHttpClient httpClient = createHttpClient(); + try + { + HttpGet getMethod = new HttpGet(uri); + getMethod.setHeader("Connection", "close"); + Credentials credentials = + new UsernamePasswordCredentials(getUserName(), password); + httpClient.getCredentialsProvider(). + setCredentials(AuthScope.ANY, credentials); + if (logger.isDebugEnabled()) + { + String logMessage = String.format( + "Getting resource %1s from the server", + uri.toString() + ); + logger.debug(logMessage); + } + HttpResponse response = httpClient.execute(getMethod); + return createResponse(response); + } + catch (IOException e) + { + String errorMessage = String.format( + "%1s resource cannot be read", + uri.toString()); + throw new XCapException(errorMessage, e); + } + finally + { + httpClient.getConnectionManager().shutdown(); + } + } + + /** + * Puts the resource to the server. + * + * @param resource the resource to be saved on the server. + * @return the server response. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public XCapHttpResponse put(XCapResource resource) + throws XCapException + { + DefaultHttpClient httpClient = createHttpClient(); + try + { + URI resourceUri = getResourceURI(resource.getId()); + HttpPut putMethod = new HttpPut(resourceUri); + putMethod.setHeader("Connection", "close"); + StringEntity stringEntity = new StringEntity(resource.getContent()); + stringEntity.setContentType(resource.getContentType()); + stringEntity.setContentEncoding("UTF-8"); + putMethod.setEntity(stringEntity); + Credentials credentials = + new UsernamePasswordCredentials(getUserName(), password); + httpClient.getCredentialsProvider(). + setCredentials(AuthScope.ANY, credentials); + if (logger.isDebugEnabled()) + { + String logMessage = String.format( + "Puting resource %1s to the server %2s", + resource.getId().toString(), + resource.getContent() + ); + logger.debug(logMessage); + } + HttpResponse response = httpClient.execute(putMethod); + return createResponse(response); + } + catch (IOException e) + { + String errorMessage = String.format( + "%1s resource cannot be put", + resource.getId().toString()); + throw new XCapException(errorMessage, e); + } + finally + { + httpClient.getConnectionManager().shutdown(); + } + } + + /** + * Deletes the resource from the server. + * + * @param resourceId resource identifier. + * @return the server response. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public XCapHttpResponse delete(XCapResourceId resourceId) + throws XCapException + { + assertConnected(); + DefaultHttpClient httpClient = createHttpClient(); + try + { + URI resourceUri = getResourceURI(resourceId); + HttpDelete deleteMethod = new HttpDelete(resourceUri); + deleteMethod.setHeader("Connection", "close"); + Credentials credentials = + new UsernamePasswordCredentials(getUserName(), password); + httpClient.getCredentialsProvider(). + setCredentials(AuthScope.ANY, credentials); + if (logger.isDebugEnabled()) + { + String logMessage = String.format( + "Deleting resource %1s from the server", + resourceId.toString() + ); + logger.debug(logMessage); + } + HttpResponse response = httpClient.execute(deleteMethod); + return createResponse(response); + } + catch (IOException e) + { + String errorMessage = String.format( + "%1s resource cannot be deleted", + resourceId.toString()); + throw new XCapException(errorMessage, e); + } + finally + { + httpClient.getConnectionManager().shutdown(); + } + } + + /** + * Gets user name. + * + * @return the user name. + */ + public String getUserName() + { + return userAddress != null ? + ((SipURI) userAddress.getURI()).getUser() : null; + } + + /** + * Gets server uri. + * + * @return the server uri. + */ + public URI getUri() + { + return uri; + } + + /** + * Gets operation timeout.The deffault value is 10 seconds. + * + * @return operation timeout. + */ + public int getTimeout() + { + return timeout; + } + + /** + * Sets operation timeout. The deffault value is 10 seconds. + * + * @param timeout operation timeout. + */ + public void setTimeout(int timeout) + { + this.timeout = timeout; + } + + /** + * Utility method throwing an exception if the user is not connected. + * + * @throws IllegalStateException if the user is not connected. + */ + protected void assertConnected() + { + if (!connected) + { + throw new IllegalStateException( + "User is not connected to the server"); + } + } + + /** + * Gets resource uri from XCAP resource identifier. + * + * @param resourceId the resource identifier. + * @return the resource uri. + */ + protected URI getResourceURI(XCapResourceId resourceId) + { + try + { + return new URI(uri.toString() + "/" + resourceId); + } + catch (URISyntaxException e) + { + throw new IllegalArgumentException( + "Invalid XCAP resource identifier", e); + } + } + + /** + * Creates HTTP client with special parameters. + * + * @return the HTTP client. + */ + private DefaultHttpClient createHttpClient() + { + DefaultHttpClient httpClient = new DefaultHttpClient(); + HttpParams httpParams = httpClient.getParams(); + HttpConnectionParams.setConnectionTimeout(httpParams, timeout); + HttpConnectionParams.setSoTimeout(httpParams, timeout); + return httpClient; + } + + /** + * Creates XCAP response from HTTP response. + * If HTTP code is 200, 201 or 409 the HTTP content would be readed. + * + * @param response the HTTP response. + * @return the XCAP response. + * @throws IOException if there is error during reading the HTTP content. + */ + private XCapHttpResponse createResponse(HttpResponse response) + throws IOException + { + XCapHttpResponse XCapHttpResponse = new XCapHttpResponse(); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == HttpStatus.SC_OK || + statusCode == HttpStatus.SC_CREATED || + statusCode == HttpStatus.SC_CONFLICT) + { + String contentType = getSingleHeaderValue(response, + HEADER_CONTENT_TYPE); + byte[] content = StreamUtils.read( + response.getEntity().getContent()); + String eTag = getSingleHeaderValue(response, HEADER_ETAG); + XCapHttpResponse.setContentType(contentType); + XCapHttpResponse.setContent(content); + XCapHttpResponse.setETag(eTag); + } + XCapHttpResponse.setHttpCode(statusCode); + return XCapHttpResponse; + } + + /** + * Reads response from http. + * @param response the response + * @return the result String. + * @throws IOException + */ + private static String readResponse(HttpResponse response) + throws IOException + { + HttpEntity responseEntity = response.getEntity(); + if (responseEntity.getContentLength() == 0) + { + return ""; + } + byte[] content = StreamUtils.read(responseEntity.getContent()); + return new String(content, "UTF-8"); + } + + /** + * Gets HTTP header value. + * + * @param response the HTTP response. + * @param headerName the header name. + * @return the header value. + */ + protected static String getSingleHeaderValue( + HttpResponse response, + String headerName) + { + Header[] headers = response.getHeaders(headerName); + if (headers != null && headers.length > 0) + { + return headers[0].getValue(); + } + return null; + } + + /** + * Analyzes the response and returns xcap error or null + * if response doesn't have it. + * + * @param response the server response. + * @return xcap error or null. + */ + protected String getXCapErrorMessage(XCapHttpResponse response) + { + int httpCode = response.getHttpCode(); + String contentType = response.getContentType(); + try + { + if (httpCode != HttpStatus.SC_CONFLICT || contentType == null || + !contentType.startsWith(XCAP_ERROR_CONTENT_TYPE)) + { + return null; + } + String content = new String(response.getContent()); + XCapErrorType xCapError = XCapErrorParser.fromXml(content); + XCapError error = xCapError.getError(); + if (error == null) + { + return null; + } + return error.getPhrase(); + } + catch (ParsingException e) + { + logger.error("XCapError cannot be parsed."); + return null; + } + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/HttpXCapClient.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/HttpXCapClient.java new file mode 100644 index 0000000..af71508 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/HttpXCapClient.java @@ -0,0 +1,105 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +import javax.sip.address.*; +import java.net.URI; + +/** + * HTTP XCAP client interface. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public interface HttpXCapClient +{ + /** + * Connects user to XCAP server. + * + * @param uri the server location. + * @param userAddress the user name. + * @param password the user password. + * @throws XCapException if there is some error during operation. + */ + public void connect(URI uri, Address userAddress, String password) + throws XCapException; + + /** + * Dicsonnects user from the XCAP server. + */ + public void dicsonnect(); + + /** + * Checks if user is connected to the XCAP server. + * + * @return true if user is connected. + */ + public boolean isConnected(); + + /** + * Gets the resource from the server. + * + * @param resourceId resource identifier. + * @return the server response. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public XCapHttpResponse get(XCapResourceId resourceId) + throws XCapException; + + /** + * Puts the resource to the server. + * + * @param resource the resource to be saved on the server. + * @return the server response. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public XCapHttpResponse put(XCapResource resource) + throws XCapException; + + /** + * Deletes the resource from the server. + * + * @param resourceId resource identifier. + * @return the server response. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public XCapHttpResponse delete(XCapResourceId resourceId) + throws XCapException; + + /** + * Gets connected user name. + * + * @return user name. + */ + public String getUserName(); + + /** + * Gets the XCAP server location. + * + * @return server location. + */ + public URI getUri(); + + /** + * Gets operation timeout. + * + * @return operation timeout. + */ + public int getTimeout(); + + /** + * Sets operation timeout. + * + * @param timeout operation timeout. + */ + public void setTimeout(int timeout); + +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/PresContentClient.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/PresContentClient.java new file mode 100644 index 0000000..70a1501 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/PresContentClient.java @@ -0,0 +1,88 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent.*; + +import java.net.*; + +/** + * XCAP pres-content client interface. + * <p/> + * Compliant with Presence Content XDM Specification v1.0 + * + * @author Grigorii Balutsel + */ +public interface PresContentClient +{ + /** + * Pres-content content type + */ + public static String CONTENT_TYPE = "application/vnd.oma.pres-content+xml"; + + /** + * Pres-content namespace + */ + public static String NAMESPACE = "urn:oma:xml:prs:pres-content"; + + /** + * Pres-content uri format + */ + public static String DOCUMENT_FORMAT = "oma_status-icon/users/%1s/%2s"; + + /** + * Puts the pres-content to the server. + * + * @param content the pres-content to be saved on the server. + * @param imageName the image name under which pres-content would be saved. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public void putPresContent(ContentType content, String imageName) + throws XCapException; + + /** + * Gets the pres-content from the server. + * + * @param imageName the image name under which pres-content is saved. + * @return the pres-content or null if there is no pres-content on the + * server. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public ContentType getPresContent(String imageName) + throws XCapException; + + /** + * Deletes the pres-content from the server. + * + * @param imageName the image name under which pres-content is saved. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public void deletePresContent(String imageName) + throws XCapException; + + /** + * Gets the pres-content image uri. + * + * @param imageName the image name under which pres-content is saved. + * @return the pres-content image uri. + * @throws IllegalStateException if the user has not been connected. + */ + public URI getPresContentImageUri(String imageName); + + /** + * Gets image from the specified uri. + * + * @param imageUri the image uri. + * @return the image. + * @throws XCapException if there is some error during operation. + */ + public byte[] getImage(URI imageUri) + throws XCapException; +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/PresRulesClient.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/PresRulesClient.java new file mode 100644 index 0000000..e0e9e62 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/PresRulesClient.java @@ -0,0 +1,64 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy.*; + +/** + * XCAP pres-rules client interface. + * <p/> + * Compliant with rfc4745, rfc5025 + * + * @author Grigorii Balutsel + */ +public interface PresRulesClient +{ + /** + * Pres-rules uri format + */ + public static String DOCUMENT_FORMAT = "pres-rules/users/%2s/presrules"; + + /** + * Pres-rules content type + */ + public static String CONTENT_TYPE = "application/auth-policy+xml"; + + /** + * Pres-rules namespace + */ + public static String NAMESPACE = "urn:ietf:params:xml:ns:pres-rules"; + + /** + * Puts the pres-rules to the server. + * + * @param presRules the pres-rules to be saved on the server. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public void putPresRules(RulesetType presRules) + throws XCapException; + + /** + * Gets the pres-rules from the server. + * + * @return the pres-rules. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public RulesetType getPresRules() + throws XCapException; + + /** + * Deletes the pres-rules from the server. + * + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public void deletePresRules() + throws XCapException; + +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/ResourceListsClient.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/ResourceListsClient.java new file mode 100644 index 0000000..b37e9c8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/ResourceListsClient.java @@ -0,0 +1,81 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists.*; + +/** + * XCAP resource-lists client interface. + * <p/> + * Compliant with rfc4825, rfc4826 + * + * @author Grigorii Balutsel + */ +public interface ResourceListsClient +{ + /** + * Resource-lists uri format. + */ + public static String DOCUMENT_FORMAT = "resource-lists/users/%2s/index"; + + /** + * Resource-lists content type. + */ + public static String RESOURCE_LISTS_CONTENT_TYPE = + "application/resource-lists+xml"; + + /** + * Resource-lists content type. + */ + public static String ELEMENT_CONTENT_TYPE = "application/xcap-el+xml"; + + /** + * Resource-lists namespace. + */ + public static String NAMESPACE = "urn:ietf:params:xml:ns:xcap-caps"; + + /** + * Puts the resource-lists to the server. + * + * @param resourceLists the resource-lists to be saved on the server. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public void putResourceLists(ResourceListsType resourceLists) + throws XCapException; + + /** + * Gets the resource-lists from the server. + * + * @return the resource-lists. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public ResourceListsType getResourceLists() + throws XCapException; + + /** + * Deletes the resource-lists from the server. + * + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public void deleteResourceLists() + throws XCapException; + + /** + * Gets the resource-lists from the server. + * + * @param anchor reference to the list. + * @return the list. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public ListType getList(String anchor) + throws XCapException; + +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapCapsClient.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapCapsClient.java new file mode 100644 index 0000000..00807aa --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapCapsClient.java @@ -0,0 +1,39 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps.*; + +/** + * XCAP xcap-caps client interface. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public interface XCapCapsClient +{ + /** + * Xcap-caps uri format + */ + public static String DOCUMENT_FORMAT = "xcap-caps/global/index"; + + /** + * Xcap-caps content type + */ + public static String CONTENT_TYPE = "application/xcap-caps+xml"; + + /** + * Gets the xcap-caps from the server. + * + * @return the xcap-caps. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public XCapCapsType getXCapCaps() + throws XCapException; +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapClient.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapClient.java new file mode 100644 index 0000000..d29a0ad --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapClient.java @@ -0,0 +1,41 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +/** + * XCAP client interface. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public interface XCapClient extends HttpXCapClient, + XCapCapsClient, ResourceListsClient, + PresRulesClient, PresContentClient +{ + /** + * Gets information about XCAP resource-lists support information. + * + * @return true if resource-lists is supported. + */ + public boolean isResourceListsSupported(); + + /** + * Gets information about XCAP pres-rules support information. + * + * @return true if pres-rules is supported. + */ + public boolean isPresRulesSupported(); + + /** + * Gets information about XCAP pres-content support information. + * + * @return true if pres-content is supported. + */ + public boolean isPresContentSupported(); + +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapClientImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapClientImpl.java new file mode 100644 index 0000000..fc3b152 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapClientImpl.java @@ -0,0 +1,751 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps.*; +import net.java.sip.communicator.util.*; +import org.apache.http.*; + +import javax.sip.address.*; +import java.io.*; +import java.net.URI; + +/** + * XCAP client implementation. + * <p/> + * Compliant with rfc4825, rfc4826, rfc5025 and Presence Content XDM + * Specification v1.0 + * + * @author Grigorii Balutsel + */ +public class XCapClientImpl extends BaseHttpXCapClient implements XCapClient +{ + /** + * Current xcap-caps. + */ + private XCapCapsType xCapCaps; + + /** + * Indicates whether or not resource-lists is supported. + */ + private boolean resourceListsSupported; + + /** + * Indicates whether or not pres-rules is supported. + */ + private boolean presRulesSupported; + + /** + * Indicates whether or not pres-content is supported. + */ + private boolean presContentSupported; + + /** + * Connects user to XCap server. Loads xcap-caps server capabilities and + * anaylyze if resource-lists, pres-rules, pres-content is supported. + * + * @param uri the server location. + * @param userAddress the user name. + * @param password the user password. + * @throws XCapException if there is some error during operation. + */ + public void connect(URI uri, Address userAddress, String password) + throws XCapException + { + super.connect(uri, userAddress, password); + try + { + xCapCaps = loadXCapCaps(); + } + catch (XCapException e) + { + dicsonnect(); + throw e; + } + for (String namespace : xCapCaps.getNamespaces().getNamespace()) + { + if (ResourceListsClient.NAMESPACE.equals(namespace)) + { + resourceListsSupported = true; + } + if (PresRulesClient.NAMESPACE.equals(namespace)) + { + //presRulesSupported = true; + } + if (PresContentClient.NAMESPACE.equals(namespace)) + { + presContentSupported = true; + } + } + } + + /** + * Disconnects user from the XCAP server. + */ + public void dicsonnect() + { + super.dicsonnect(); + xCapCaps = null; + resourceListsSupported = false; + } + + /** + * Puts the resource-lists to the server. + * + * @param resourceLists the resource-lists to be saved on the server. + * @throws IllegalStateException if the user has not been connected, or + * resource-lists is not supported. + * @throws XCapException if there is some error during operation. + */ + public void putResourceLists(ResourceListsType resourceLists) + throws XCapException + { + assertConnected(); + assertResourceListsSupported(); + String resourceListsDocument = getResourceListsDocument(); + XCapResourceId resourceId = new XCapResourceId(resourceListsDocument); + try + { + if (resourceLists.getList().size() == 0) + { + deleteResourceLists(); + return; + } + String xml = ResourceListsParser.toXml(resourceLists); + XCapResource resource = new XCapResource(resourceId, xml, + XCapCapsClient.CONTENT_TYPE); + // Put resource-lists to the server + putResource(resource); + } + catch (ParsingException e) + { + throw new XCapException("ResourceLists cannot be parsed", e); + } + } + + /** + * Gets the resource-lists from the server. + * + * @return the resource-lists. + * @throws IllegalStateException if the user has not been connected, or + * resource-lists is not supported. + * @throws XCapException if there is some error during operation. + */ + public ResourceListsType getResourceLists() + throws XCapException + { + assertConnected(); + assertResourceListsSupported(); + String resourceListsDocument = getResourceListsDocument(); + XCapResourceId resourceId = new XCapResourceId(resourceListsDocument); + try + { + String xml = getResource(resourceId, + ResourceListsClient.RESOURCE_LISTS_CONTENT_TYPE); + if (xml == null) + { + return new ResourceListsType(); + } + return ResourceListsParser.fromXml(xml); + } + catch (ParsingException e) + { + throw new XCapException("ResourceLists cannot be parsed", e); + } + } + + /** + * Deletes the resource-lists from the server. + * + * @throws IllegalStateException if the user has not been connected, or + * resource-lists is not supported. + * @throws XCapException if there is some error during operation. + */ + public void deleteResourceLists() + throws XCapException + { + assertConnected(); + assertResourceListsSupported(); + String resourceListsDocument = getResourceListsDocument(); + XCapResourceId resourceId = new XCapResourceId(resourceListsDocument); + deleteResource(resourceId); + } + + /** + * Gets the resource-lists from the server. + * + * @param anchor reference to the list. + * @return the list. + * @throws IllegalStateException if the user has not been connected, or + * resource-lists is not supported. + * @throws XCapException if there is some error during operation. + */ + public ListType getList(String anchor) + throws XCapException + { + assertConnected(); + assertResourceListsSupported(); + XCapResourceId resourceId = XCapResourceId.create(anchor); + return null; + // TODO: uncomment after implementation +// try +// { +// // Load list from the server +// String xml = getResource(resourceId, +// ResourceListsClient.ELEMENT_CONTENT_TYPE); +// if (xml == null) +// { +// throw new XCapException(resourceId.toString() + "wasn't find"); +// } +// return (ListType) XmlUtils.createDocument(ListType.class, xml); +// } +// catch (JAXBException e) +// { +// throw new XCapException("ResourceLists cannot be parsed", e); +// } + } + + /** + * Gets the xcap-caps from the server. + * + * @return the xcap-caps. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + public XCapCapsType getXCapCaps() + throws XCapException + { + assertConnected(); + return xCapCaps; + } + + /** + * Loads the xcap-caps from the server. + * + * @return the xcap-caps. + * @throws IllegalStateException if the user has not been connected. + * @throws XCapException if there is some error during operation. + */ + private XCapCapsType loadXCapCaps() + throws XCapException + { + String xCapCapsDocument = getXCapCapsDocument(); + XCapResourceId resourceId = new XCapResourceId(xCapCapsDocument); + try + { + // Load xcap-caps from the server + String xml = getResource(resourceId, XCapCapsClient.CONTENT_TYPE); + if (xml == null) + { + return new XCapCapsType(); + } + return XCapCapsParser.fromXml(xml); + } + catch (ParsingException e) + { + throw new XCapException("XCapCapsType cannot be parsed", e); + } + } + + /** + * Gets the pres-rules from the server. + * + * @return the pres-rules. + * @throws IllegalStateException if the user has not been connected, or + * pres-rules is not supported. + * @throws XCapException if there is some error during operation. + */ + public RulesetType getPresRules() + throws XCapException + { + assertConnected(); + assertPresRulesSupported(); + String presRulesDocument = getPresRulesDocument(); + XCapResourceId resourceId = new XCapResourceId(presRulesDocument); + try + { + // Load pres-rules from the server + String xml = getResource(resourceId, PresRulesClient.CONTENT_TYPE); + if (xml == null) + { + return new RulesetType(); + } + // TODO: uncomment after implementation + //return (RulesetType) XmlUtils.createDocument(RulesetType.class, xml); + return null; + } + catch (Exception e) + { + throw new XCapException("PresRules cannot be parsed", e); + } + } + + /** + * Puts the pres-rules to the server. + * + * @param presRules the pres-rules to be saved on the server. + * @throws IllegalStateException if the user has not been connected, or + * pres-rules is not supported. + * @throws XCapException if there is some error during operation. + */ + public void putPresRules(RulesetType presRules) + throws XCapException + { + assertConnected(); + assertPresRulesSupported(); + String resourceListsDocument = getPresRulesDocument(); + XCapResourceId resourceId = new XCapResourceId(resourceListsDocument); + // TODO: uncomment after implementation +// try +// { +// String xml = XmlUtils.toXml(RulesetType.class, presRules); +// XCapResource resource = new XCapResource(resourceId, xml, +// PresRulesClient.CONTENT_TYPE); +// // Put pres-rules to the server +// putResource(resource); +// } +// catch (JAXBException e) +// { +// throw new XCapException("PresRules cannot be parsed", e); +// } + } + + /** + * Deletes the pres-rules from the server. + * + * @throws IllegalStateException if the user has not been connected, or + * pres-rules is not supported. + * @throws XCapException if there is some error during operation. + */ + public void deletePresRules() + throws XCapException + { + assertConnected(); + assertResourceListsSupported(); + String presRulesDocument = getPresRulesDocument(); + XCapResourceId resourceId = new XCapResourceId(presRulesDocument); + deleteResource(resourceId); + } + + /** + * Puts the pres-content to the server. + * + * @param content the pres-content to be saved on the server. + * @param imageName the image name under which pres-content would be saved. + * @throws IllegalStateException if the user has not been connected, or + * pres-content is not supported. + * @throws XCapException if there is some error during operation. + */ + public void putPresContent(ContentType content, String imageName) + throws XCapException + { + assertConnected(); + assertPresContentSupported(); + String presContentDocument = getPresContentDocument(imageName); + XCapResourceId resourceId = new XCapResourceId(presContentDocument); + try + { + String xml = PresContentParser.toXml(content); + XCapResource resource = new XCapResource(resourceId, xml, + PresContentClient.CONTENT_TYPE); + // Put pres-content to the server + putResource(resource); + } + catch (ParsingException e) + { + throw new XCapException("ContentType cannot be parsed", e); + } + } + + /** + * Gets the pres-content from the server. + * + * @param imageName the image name under which pres-content is saved. + * @return the pres-content or null if there is no pres-content on + * the server. + * @throws IllegalStateException if the user has not been connected, or + * pres-content is not supported. + * @throws XCapException if there is some error during operation. + */ + public ContentType getPresContent(String imageName) + throws XCapException + { + assertConnected(); + assertPresContentSupported(); + String presContentDocument = getPresContentDocument(imageName); + XCapResourceId resourceId = new XCapResourceId(presContentDocument); + try + { + // Load pres-content from the server + XCapHttpResponse response = this.get(resourceId); + int httpCode = response.getHttpCode(); + String contentType = response.getContentType(); + byte[] content = response.getContent(); + // Analyze the responce + if (httpCode != HttpStatus.SC_OK) + { + if (httpCode == HttpStatus.SC_NOT_FOUND) + { + return null; + } + String errorMessage = String.format( + "Error %1s while getting %1s PresContent from XCAP server", + httpCode, + resourceId.toString()); + throw new XCapException(errorMessage); + } + if (!contentType.equals(PresContentClient.CONTENT_TYPE)) + { + String errorMessage = String.format( + "XCAP server returns invalid PresContent content type: %1s", + contentType); + throw new XCapException(errorMessage); + } + if (content == null || content.length == 0) + { + throw new XCapException( + "XCAP server returns invalid PresContent content"); + } + try + { + return PresContentParser.fromXml(new String(content, "UTF-8")); + } + catch (ParsingException e) + { + // TODO: remove it after the sip2sip fixes + // The only server that supports it is sip2sip server. + // They do not follow for 100% percent the RFC + ContentType presContent = new ContentType(); + DataType data = new DataType(); + data.setValue(new String(Base64.encode(content))); + presContent.setData(data); + return presContent; + } + } + catch (IOException e) + { + String errorMessage = String.format( + "%1s resource cannot be read", + resourceId.toString()); + throw new XCapException(errorMessage, e); + } + } + + /** + * Deletes the pres-content from the server. + * + * @param imageName the image name under which pres-content is saved. + * @throws IllegalStateException if the user has not been connected, or + * pres-content is not supported. + * @throws XCapException if there is some error during operation. + */ + public void deletePresContent(String imageName) + throws XCapException + { + assertConnected(); + assertPresContentSupported(); + String presContentDocument = getPresContentDocument(imageName); + XCapResourceId resourceId = new XCapResourceId(presContentDocument); + deleteResource(resourceId); + } + + /** + * Gets the pres-content image uri. + * + * @param imageName the image name under which pres-content is saved. + * @return the pres-content image uri. + * @throws IllegalStateException if the user has not been connected. + */ + public URI getPresContentImageUri(String imageName) + { + assertConnected(); + String presContentDocument = getPresContentDocument(imageName); + XCapResourceId resourceId = new XCapResourceId(presContentDocument); + return getResourceURI(resourceId); + } + + /** + * Gets image from the specified uri. + * + * @param imageUri the image uri. + * @return the image. + * @throws XCapException if there is some error during operation. + */ + public byte[] getImage(URI imageUri) + throws XCapException + { + assertConnected(); + XCapHttpResponse response = this.get(imageUri); + int httpCode = response.getHttpCode(); + byte[] content = response.getContent(); + // Analyze the responce + if (httpCode != HttpStatus.SC_OK) + { + String errorMessage = String.format( + "Error %1s while getting %2s image from the server", + httpCode, + imageUri); + throw new XCapException(errorMessage); + } + return content; + } + + /** + * Utility method throwing an exception if the resource-lists + * is not supported. + * + * @throws IllegalStateException if the user is not connected. + */ + protected void assertResourceListsSupported() + { + if (!resourceListsSupported) + { + throw new IllegalStateException( + "XCAP server doesn't support resource-lists"); + } + } + + /** + * Utility method throwing an exception if the pres-rules + * is not supported. + * + * @throws IllegalStateException if the user is not connected. + */ + protected void assertPresRulesSupported() + { + if (!presRulesSupported) + { + throw new IllegalStateException( + "XCAP server doesn't support pres-rules"); + } + } + + /** + * Utility method throwing an exception if the pres-content + * is not supported. + * + * @throws IllegalStateException if the user is not connected. + */ + protected void assertPresContentSupported() + { + if (!presContentSupported) + { + throw new IllegalStateException( + "XCAP server doesn't support pres-content"); + } + } + + /** + * Puts XCAP resources to the server. Analyzes HTTP code and tryes to get + * xcap-error if possible. + * + * @param resource the resource. + * @throws XCapException if there is some error during operation. + */ + private void putResource(XCapResource resource) + throws XCapException + { + XCapHttpResponse response = this.put(resource); + int httpCode = response.getHttpCode(); + if (httpCode != HttpStatus.SC_OK && httpCode != HttpStatus.SC_CREATED) + { + String errorMessage; + String xCapErrorMessage = getXCapErrorMessage(response); + if (xCapErrorMessage != null) + { + errorMessage = String.format( + "Error %1s while putting %2s to XCAP server. %3s", + httpCode, + resource.getId().toString(), + xCapErrorMessage); + } + else + { + errorMessage = String.format( + "Error %1s while putting %2s to XCAP server", + httpCode, + resource.getId().toString()); + } + throw new XCapException(errorMessage); + } + } + + /** + * Gets XCAP resources from the server. Analyzes HTTP code and tryes to get + * xcap-error if possible. + * + * @param resourceId the resource identifier. + * @param contentType the resource content-type. + * @return XCAP resource. + * @throws XCapException if there is some error during operation. + */ + private String getResource(XCapResourceId resourceId, String contentType) + throws XCapException + { + try + { + // Load resource from the server + XCapHttpResponse response = this.get(resourceId); + int httpCode = response.getHttpCode(); + byte[] content = response.getContent(); + // Analyze the response + if (httpCode != HttpStatus.SC_OK) + { + if (httpCode == HttpStatus.SC_NOT_FOUND) + { + return null; + } + String errorMessage; + String xCapErrorMessage = getXCapErrorMessage(response); + if (xCapErrorMessage != null) + { + errorMessage = String.format( + "Error %1s while getting %2s from XCAP server. %3s", + httpCode, + resourceId.toString(), + xCapErrorMessage); + } + else + { + errorMessage = String.format( + "Error %1s while getting %2s from XCAP server", + httpCode, + resourceId.toString()); + } + throw new XCapException(errorMessage); + } + if (!contentType.equals(response.getContentType())) + { + String errorMessage = String.format( + "XCAP server returns invalid content type: %1s", + contentType); + throw new XCapException(errorMessage); + } + if (content == null || content.length == 0) + { + return null; + } + return new String(content, "UTF-8"); + } + catch (IOException e) + { + String errorMessage = String.format( + "%1s resource cannot be read", + resourceId.toString()); + throw new XCapException(errorMessage, e); + } + } + + /** + * Deletes XCAP resources from the server. Analyzes HTTP code and tryes to + * get xcap-error if possible. + * + * @param resourceId the resource identifier. + * @throws XCapException if there is some error during operation. + */ + private void deleteResource(XCapResourceId resourceId) + throws XCapException + { + XCapHttpResponse response = this.delete(resourceId); + int httpCode = response.getHttpCode(); + if (httpCode != HttpStatus.SC_OK && httpCode != HttpStatus.SC_NOT_FOUND) + { + String errorMessage; + String xCapErrorMessage = getXCapErrorMessage(response); + if (xCapErrorMessage != null) + { + errorMessage = String.format( + "Error %1s while deleting %2s resource from XCAP server. %3s", + httpCode, + resourceId.toString(), + xCapErrorMessage); + } + else + { + errorMessage = String.format( + "Error %1s while deleting %2s resource from XCAP server", + httpCode, + resourceId.toString()); + } + throw new XCapException(errorMessage); + } + } + + /** + * Returns resource lists uri according to rfc4825. + * + * @return resource lists uri. + */ + private String getResourceListsDocument() + { + return String.format(ResourceListsClient.DOCUMENT_FORMAT, + userAddress.getURI().toString()); + } + + /** + * Returns xcap-caps uri according to rfc4825. + * + * @return xcap-caps uri. + */ + private String getXCapCapsDocument() + { + return XCapCapsClient.DOCUMENT_FORMAT; + } + + /** + * Gets resource-lists uri according to rfc5025. + * + * @return resource-lists uri. + */ + private String getPresRulesDocument() + { + return String.format(PresRulesClient.DOCUMENT_FORMAT, + userAddress.getURI().toString()); + } + + /** + * Gets pres-content uri according to rfc. + * + * @param imageName the pres-content image name. + * @return pres-content uri. + */ + private String getPresContentDocument(String imageName) + { + return String.format(PresContentClient.DOCUMENT_FORMAT, + userAddress.getURI().toString(), imageName); + } + + /** + * Indicates whether or not pres-rules is supported. + */ + public boolean isResourceListsSupported() + { + assertConnected(); + return resourceListsSupported; + } + + /** + * Indicates whether or not pres-rules is supported. + */ + public boolean isPresRulesSupported() + { + assertConnected(); + return presRulesSupported; + } + + /** + * Indicates whether or not pres-rules is supported. + */ + public boolean isPresContentSupported() + { + return presContentSupported; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapException.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapException.java new file mode 100644 index 0000000..77677d7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapException.java @@ -0,0 +1,63 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +/** + * Exceptions of this class get thrown whenever an error occurs while operating + * with XCAP server. + * + * @author Grigorii Balutsel + */ +public class XCapException extends Exception +{ + /** + * Creates a new <code>XCapException</code> instance + * which does not give a human-readable explanation why the operation is + * not supported. + */ + public XCapException() + { + } + + /** + * Creates a new <code>XCapException</code> instance whith human-readable + * explanation. + * + * @param message the detailed message explaining any particular details as + * to why is not the specified operation supported or null if + * no particular details exist. + */ + public XCapException(String message) + { + super(message); + } + + /** + * Creates a new <code>XCapException</code> instance with human-readable + * explanation and the original cause of the problem. + * + * @param message the detailed message explaining any particular details as + * to why is not the specified operation supported or null if + * no particular details exist. + * @param cause the original cause of the problem. + */ + public XCapException(String message, Throwable cause) + { + super(message, cause); + } + + /** + * Creates a new <code>XCapException</code> instance with the original cause + * of the problem. + * + * @param cause the original cause of the problem. + */ + public XCapException(Throwable cause) + { + super(cause); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapHttpResponse.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapHttpResponse.java new file mode 100644 index 0000000..1d8314d --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapHttpResponse.java @@ -0,0 +1,115 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +/** + * XCAP HTTP response. + * + * @author Grigorii Balutsel + */ +public class XCapHttpResponse +{ + /** + * HTTP code. + */ + private int httpCode; + + /** + * HTTP Content-Type + */ + private String contentType; + + /** + * HTTP response content. + */ + private byte[] content; + + /** + * HTTP ETag. + */ + private String eTag; + + /** + * Gets HTTP code. + * + * @return the HTTP code. + */ + public int getHttpCode() + { + return httpCode; + } + + /** + * Sets HTTP code. + * + * @param httpCode the HTTP code. + */ + void setHttpCode(int httpCode) + { + this.httpCode = httpCode; + } + + /** + * Gets HTTP Content-Type. + * + * @return the HTTP Content-Type. + */ + public String getContentType() + { + return contentType; + } + + /** + * Sets HTTP Content-Type. + * + * @param contentType the HTTP Content-Type. + */ + void setContentType(String contentType) + { + this.contentType = contentType; + } + + /** + * Gets HTTP response content. + * + * @return the HTTP response content. + */ + public byte[] getContent() + { + return content; + } + + /** + * Sets HTTP response content. + * + * @param content the HTTP response content. + */ + void setContent(byte[] content) + { + this.content = content; + } + + /** + * Gets HTTP ETag. + * + * @return the HTTP ETag. + */ + public String getETag() + { + return eTag; + } + + /** + * Sets HTTP ETag. + * + * @param eTag the HTTP ETag. + */ + void setETag(String eTag) + { + this.eTag = eTag; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapResource.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapResource.java new file mode 100644 index 0000000..877f405 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapResource.java @@ -0,0 +1,77 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +/** + * XCAP resource. + * </p> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class XCapResource +{ + /** + * XCAP resource identifier. + */ + private XCapResourceId id; + + /** + * XCAP resource content in UTF-8 enconding. + */ + private String content; + + /** + * XCAP resource content type. + */ + private String contentType; + + /** + * Creates XCAP resource with XCAP resource identifier, XCAP resource + * content and content-type. + * + * @param id the XCAP resource identifier. + * @param content the XCAP resource content. + * @param contentType the XCAP resource content type. + */ + public XCapResource(XCapResourceId id, String content, String contentType) + { + this.id = id; + this.content = content; + this.contentType = contentType; + } + + /** + * Gets XCAP resource identifier. + * + * @return the XCAP resource identifier. + */ + public XCapResourceId getId() + { + return id; + } + + /** + * Gets XCAP resource content. + * + * @return the XCAP resource content. + */ + public String getContent() + { + return content; + } + + /** + * Gets XCAP resource content type. + * + * @return the XCAP resource content type. + */ + public String getContentType() + { + return contentType; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapResourceId.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapResourceId.java new file mode 100644 index 0000000..de9fa31 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/XCapResourceId.java @@ -0,0 +1,118 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap; + +/** + * XCAP resource identifier. + * + * @author Grigorii Balutsel + */ +public class XCapResourceId +{ + /** + * Delimeter between document and node selectors. + */ + private static String DELIMETER = "/~~"; + + /** + * Document selector. + */ + private String document; + + /** + * Node selector. + */ + private String node; + + /** + * Creates XCAP resource identifier with document selector. + * + * @param document the document selector. + */ + public XCapResourceId(String document) + { + this(document, null); + } + + /** + * Creates XCAP resource identifier with document and node selector. + * + * @param document the document selector. + * @param node the node selector. + */ + public XCapResourceId(String document, String node) + { + if (document == null || document.length() == 0) + { + throw new IllegalArgumentException( + "XCAP resource document cannot be null or empty"); + } + this.document = document; + this.node = node; + } + + /** + * Gets document selector. + * + * @return the document selector. + */ + public String getDocument() + { + return document; + } + + /** + * Gets node selector. + * + * @return the node selector. + */ + public String getNode() + { + return node; + } + + /** + * Gets XCAP resource identifier object as single string. + * + * @return the XCAP resource identifier object as single string. + */ + public String toString() + { + StringBuilder builder = new StringBuilder(document); + if (node != null && node.length() != 0) + { + builder.append(DELIMETER).append(node); + } + return builder.toString(); + } + + /** + * Creates XCAP resource identifier object from single string. + * + * @param resourceId the XCAP resource identifier as single string. + * @return the XCAP resource identifier. + * @throws IllegalArgumentException if resourceId is null or emty or has + * invalid format. + */ + public static XCapResourceId create(String resourceId) + { + if (resourceId == null || resourceId.trim().length() == 0) + { + throw new IllegalArgumentException( + "Resource identifier cannot be null or empty"); + } + int index = resourceId.indexOf(DELIMETER); + if (index == -1) + { + throw new IllegalArgumentException( + "Resource identifier has invalid format"); + } + String document = resourceId.substring(0, index); + String node = resourceId.substring(index + DELIMETER.length()); + return new XCapResourceId(document, node); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/ParsingException.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/ParsingException.java new file mode 100644 index 0000000..124fe7a --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/ParsingException.java @@ -0,0 +1,63 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model; + +/** + * Exceptions of this class get thrown whenever an error occurs while operating + * with XCAP server. + * + * @author Grigorii Balutsel + */ +public class ParsingException extends Exception +{ + /** + * Creates a new <code>XCapException</code> instance + * which does not give a human-readable explanation why the operation is + * not supported. + */ + public ParsingException() + { + } + + /** + * Creates a new <code>XCapException</code> instance whith human-readable + * explanation. + * + * @param message the detailed message explaining any particular details as + * to why is not the specified operation supported or null if + * no particular details exist. + */ + public ParsingException(String message) + { + super(message); + } + + /** + * Creates a new <code>XCapException</code> instance with human-readable + * explanation and the original cause of the problem. + * + * @param message the detailed message explaining any particular details as + * to why is not the specified operation supported or null if + * no particular details exist. + * @param cause the original cause of the problem. + */ + public ParsingException(String message, Throwable cause) + { + super(message, cause); + } + + /** + * Creates a new <code>XCapException</code> instance with the original cause + * of the problem. + * + * @param cause the original cause of the problem. + */ + public ParsingException(Throwable cause) + { + super(cause); + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/StringUtils.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/StringUtils.java new file mode 100644 index 0000000..a7298e5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/StringUtils.java @@ -0,0 +1,95 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model; + +import java.io.*; + +/** + * Utility class that helps to work with <tt>String</tt> class. + * + * @author Grigorii Balutsel + */ +public final class StringUtils +{ + /** + * This class cannot be instanced. + */ + private StringUtils() + { + } + + /** + * Indicates whether string is null or empty. + * + * @param s the string to analyze. + * @return true if string is null or empty. + */ + public static boolean isNullOrEmpty(String s) + { + return isNullOrEmpty(s, true); + } + + /** + * Indicates whether string is null or empty. + * + * @param s the string to analyze. + * @param trim indicates whether to trim the string. + * @return true if string is null or empty. + */ + public static boolean isNullOrEmpty(String s, boolean trim) + { + if (s == null) + { + return true; + } + if (trim) + { + s = s.trim(); + } + return s.length() == 0; + } + + /** + * Indicates whether strings are equal. + * + * @param s1 the string to analyze. + * @param s2 the string to analyze. + * @return true if string are equal. + */ + public static boolean isEquals(String s1, String s2) + { + return (s1 == null && s2 == null) || (s1 != null && s1.equals(s2)); + } + + /** + * Creates <tt>InputStream</tt> from the string in UTF8 encoding. + * + * @param string the string to convert. + * @return the <tt>InputStream</tt>. + * @throws UnsupportedEncodingException if UTF8 is unsupported. + */ + public static InputStream fromString(String string) + throws UnsupportedEncodingException + { + return fromString(string, "UTF-8"); + } + + /** + * Creates <tt>InputStream</tt> from the string in the specified encoding. + * + * @param string the string to convert. + * @param encoding the encoding + * @return the <tt>InputStream</tt>. + * @throws UnsupportedEncodingException if the encoding is unsupported. + */ + public static InputStream fromString(String string, String encoding) + throws UnsupportedEncodingException + { + byte[] bytes = string.getBytes(encoding); + return new ByteArrayInputStream(bytes); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/XmlUtils.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/XmlUtils.java new file mode 100644 index 0000000..2a1dc80 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/XmlUtils.java @@ -0,0 +1,299 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model; + +import static net.java.sip.communicator.impl.protocol.sip.xcap.model.StringUtils.*; +import org.w3c.dom.*; + +import javax.xml.*; +import javax.xml.namespace.QName; +import javax.xml.transform.*; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import static javax.xml.XMLConstants.*; +import javax.xml.parsers.*; +import java.io.*; +import java.util.*; + +/** + * Utility class that helps to convert classes into xml and from xml to the + * classes. + * + * @author Grigorii Balutsel + */ +public final class XmlUtils +{ + /** + * Indicates whether namespace is one of the standart xml namespace. + * + * @param namespace the namespace to analyze. + * @return true is namespace is one of the standart xml namespace otherwise + * false. + */ + public static boolean isStandartXmlNamespace(String namespace) + { + namespace = normalizeNamespace(namespace); + return normalizeNamespace(XML_NS_URI).equals(namespace) + || normalizeNamespace(XMLNS_ATTRIBUTE_NS_URI).equals(namespace) + || normalizeNamespace(W3C_XML_SCHEMA_NS_URI).equals(namespace) + || normalizeNamespace(W3C_XML_SCHEMA_INSTANCE_NS_URI) + .equals(namespace); + } + + /** + * Gets the node namespace. + * + * @param node the <tt>Element</tt> or <tt>Attr</tt> node to analyze. + * @return the node namespace or null. + */ + public static String getNamespaceUri(Node node) + { + String prefix = node.getPrefix(); + String namespaceUri = node.getNamespaceURI(); + if (!isNullOrEmpty(namespaceUri)) + { + return normalizeNamespace(namespaceUri); + } + if (XMLConstants.XMLNS_ATTRIBUTE.equals(node.getNodeName()) || + XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) + { + return normalizeNamespace(XMLNS_ATTRIBUTE_NS_URI); + } + Element rootElement = node.getOwnerDocument().getDocumentElement(); + Node parentNode = null; + while (parentNode != rootElement) + { + if (parentNode == null) + { + if (node.getNodeType() == Node.ATTRIBUTE_NODE) + { + parentNode = ((Attr) node).getOwnerElement(); + } + else if (node.getNodeType() == Node.ELEMENT_NODE) + { + parentNode = node.getParentNode(); + } + else + { + return null; + } + } + else + { + parentNode = parentNode.getParentNode(); + } + String parentPrefix = parentNode.getPrefix(); + String parentNamespaceUri = parentNode.getNamespaceURI(); + if (isNullOrEmpty(prefix)) + { + Node xmlnsAttribute = + parentNode.getAttributes().getNamedItem("xmlns"); + if (xmlnsAttribute != null) + { + return ((Attr) xmlnsAttribute).getValue(); + } + } + else if (isEquals(prefix, parentPrefix)) + { + if (!isNullOrEmpty(parentNamespaceUri)) + { + return normalizeNamespace(parentNamespaceUri); + } + } + } + if ("xml".equals(prefix)) + { + return normalizeNamespace(XML_NS_URI); + } + return null; + } + + /** + * Normalizes the namespace. + * + * @param namespace the namespace to normalize. + * @return normalized namespace. + */ + private static String normalizeNamespace(String namespace) + { + if (namespace.endsWith("/")) + { + return namespace.substring(0, namespace.length() - 1); + } + return namespace; + } + + /** + * Creates copy of the node in the scope of the document. Attributes, + * Elements, and Tex will be include. + * + * @param document the xml document. + * @param node the node to create the copy. + * @return the copy of the node in the scope of the document. + * @throws Exception if there is some error during processing. + */ + public static Node importNode(Document document, Node node) + throws Exception + { + if (node.getNodeType() == Node.ATTRIBUTE_NODE) + { + String namespace = getNamespaceUri(node); + if (namespace == null) + { + throw new Exception("Namespace cannot be null"); + } + Attr attribute = + document.createAttributeNS(namespace, node.getNodeName()); + attribute.setValue(((Attr) node).getValue()); + return attribute; + } + else if (node.getNodeType() == Node.ELEMENT_NODE) + { + String namespace = getNamespaceUri(node); + if (namespace == null) + { + throw new Exception("Namespace cannot be null"); + } + + Element element = + document.createElementNS(namespace, node.getNodeName()); + + // Process attributes + NamedNodeMap attributes = node.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + if ("xmlns".equals(attribute.getPrefix())) + { + continue; + } + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("entry element is invalid"); + } + element.getAttributes() + .setNamedItemNS(importNode(document, attribute)); + } + // Process elements + NodeList childNodes = node.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node childNode = childNodes.item(i); + if (childNode.getNodeType() == Node.ELEMENT_NODE) + { + element.appendChild(importNode(document, childNode)); + } + else if (childNode.getNodeType() == Node.TEXT_NODE) + { + element.appendChild(document.createTextNode( + childNode.getTextContent())); + } + } + return element; + } + else + { + throw new Exception("Node cannot be processed " + node.toString()); + } + } + + /** + * Creates W3C Document. + * + * @return the W3C Document. + * @throws Exception is there is some error during operation. + */ + public static Document createDocument() + throws Exception + { + return createDocument(null); + } + + /** + * Creates W3C Document from the xml. + * + * @param xml the xml that needs to be converted. + * @return the W3C Document. + * @throws Exception is there is some error during operation. + */ + public static Document createDocument(String xml) + throws Exception + { + DocumentBuilderFactory builderFactory = + DocumentBuilderFactory.newInstance(); + builderFactory.setNamespaceAware(true); + DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder(); + if (!isNullOrEmpty(xml)) + { + InputStream input = fromString(xml); + return documentBuilder.parse(input); + } + else + { + return documentBuilder.newDocument(); + } + } + + /** + * Creates XML from W3C Document from the xml. + * + * @param document the xml that needs to be converted. + * @return the XML. + * @throws Exception is there is some error during operation. + */ + public static String createXml(Document document) + throws Exception + { + TransformerFactory transformerFactory = + TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + StringWriter stringWriter = new StringWriter(); + StreamResult result = new StreamResult(stringWriter); + DOMSource source = new DOMSource(document); + transformer.transform(source, result); + return stringWriter.toString(); + } + + /** + * Processes any attributes and add them into the element. + * + * @param element the element where to add the attributes. + * @param anyAttributes the any attributes to process. + */ + public static void processAnyAttributes( + Element element, Map<QName, String> anyAttributes) + { + for (Map.Entry<QName, String> attribute : anyAttributes.entrySet()) + { + String localName = attribute.getKey().getLocalPart(); + String prefix = attribute.getKey().getPrefix(); + String namespace = attribute.getKey().getNamespaceURI(); + element.setAttributeNS(namespace, prefix + ":" + localName, + attribute.getValue()); + } + } + + /** + * Processes any element and add them into the element. + * + * @param element the element where to add the elements. + * @param any the any elements to process. + * @throws Exception if there is some error during processing. + */ + public static void processAny(Element element, List<Element> any) + throws Exception + { + for (Element anyElement : any) + { + Element importedElement = + (Element) importNode(element.getOwnerDocument(), + anyElement); + element.appendChild(importedElement); + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ActionsType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ActionsType.java new file mode 100644 index 0000000..8d73c0e --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ActionsType.java @@ -0,0 +1,86 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.presrules.*; + +import java.util.*; + +/** + * <p>Java class for extensibleType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="extensibleType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <any/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "actionsType", propOrder = { +// "subHandling", +// "any" +// }) +public class ActionsType +{ + +// @XmlElement(name = "sub-handling", +// namespace = "urn:ietf:params:xml:ns:pres-rules", required = false) + protected SubHandlingType subHandling; + +// @XmlAnyElement(lax = true) + protected List<Object> any; + + /** + * Gets the value of the any property. + * <p/> + * <p/> + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a <CODE>set</CODE> method for the any property. + * <p/> + * <p/> + * For example, to add a new item, do as follows: + * <pre> + * getAny().add(newItem); + * </pre> + * <p/> + * <p/> + * <p/> + * Objects of the following type(s) are allowed in the list + * {@link org.w3c.dom.Element } + * {@link Object } + */ + public List<Object> getAny() + { + if (any == null) + { + any = new ArrayList<Object>(); + } + return this.any; + } + + public SubHandlingType getSubHandling() + { + return subHandling; + } + + public void setSubHandling(SubHandlingType subHandling) + { + this.subHandling = subHandling; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ConditionsType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ConditionsType.java new file mode 100644 index 0000000..fcd8980 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ConditionsType.java @@ -0,0 +1,63 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +import java.util.*; + +/** + * <p>Java class for conditionsType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="conditionsType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <choice maxOccurs="unbounded"> + * <element name="identity" type="{urn:ietf:params:xml:ns:common-policy}identityType" minOccurs="0"/> + * <element name="sphere" type="{urn:ietf:params:xml:ns:common-policy}sphereType" minOccurs="0"/> + * <element name="validity" type="{urn:ietf:params:xml:ns:common-policy}validityType" minOccurs="0"/> + * <any/> + * </choice> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "conditionsType", propOrder = { +// "identityOrSphereOrValidity" +// }) +public class ConditionsType +{ + +// @XmlElementRefs({ +// @XmlElementRef(name = "identity", +// namespace = "urn:ietf:params:xml:ns:common-policy", +// type = JAXBElement.class), +// @XmlElementRef(name = "sphere", +// namespace = "urn:ietf:params:xml:ns:common-policy", +// type = JAXBElement.class), +// @XmlElementRef(name = "validity", +// namespace = "urn:ietf:params:xml:ns:common-policy", +// type = JAXBElement.class) +// }) +// @XmlAnyElement(lax = true) + protected List<Object> identityOrSphereOrValidity; + + + public List<Object> getIdentityOrSphereOrValidity() + { + if (identityOrSphereOrValidity == null) + { + identityOrSphereOrValidity = new ArrayList<Object>(); + } + return this.identityOrSphereOrValidity; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ExceptType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ExceptType.java new file mode 100644 index 0000000..67f0a49 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ExceptType.java @@ -0,0 +1,83 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +//import javax.xml.bind.annotation.*; + +/** + * <p>Java class for exceptType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="exceptType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="domain" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="id" type="{http://www.w3.org/2001/XMLSchema}anyURI" /> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "exceptType") +public class ExceptType +{ + +// @XmlAttribute + protected String domain; + +// @XmlAttribute + protected String id; + + /** + * Gets the value of the domain property. + * + * @return possible object is + * {@link String } + */ + public String getDomain() + { + return domain; + } + + /** + * Sets the value of the domain property. + * + * @param value allowed object is + * {@link String } + */ + public void setDomain(String value) + { + this.domain = value; + } + + /** + * Gets the value of the id property. + * + * @return possible object is + * {@link String } + */ + public String getId() + { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value allowed object is + * {@link String } + */ + public void setId(String value) + { + this.id = value; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/IdentityType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/IdentityType.java new file mode 100644 index 0000000..0ed154a --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/IdentityType.java @@ -0,0 +1,109 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +import org.w3c.dom.Element; + +//import javax.xml.bind.*; +//import javax.xml.bind.annotation.*; +import java.util.*; + +/** + * <p>Java class for identityType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="identityType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <choice maxOccurs="unbounded"> + * <element name="one" type="{urn:ietf:params:xml:ns:common-policy}oneType"/> + * <element name="many" type="{urn:ietf:params:xml:ns:common-policy}manyType"/> + * <any/> + * </choice> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ + +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "identityType", propOrder = { +// "oneList", +// "manyList", +// "any" +// }) +//@XmlRootElement(name = "identity", +// namespace = "urn:ietf:params:xml:ns:common-policy") +public class IdentityType +{ +// @XmlElementRef(name = "one", +// namespace = "urn:ietf:params:xml:ns:common-policy", +// type = OneType.class) + protected List<OneType> oneList; + +// @XmlElementRef(name = "many", +// namespace = "urn:ietf:params:xml:ns:common-policy", +// type = ManyType.class) + protected List<ManyType> manyList; + +// @XmlAnyElement(lax = true) + protected List<Object> any; + +// /** +// * Gets the value of the oneOrManyOrAny property. +// * <p/> +// * <p/> +// * This accessor method returns a reference to the live list, +// * not a snapshot. Therefore any modification you make to the +// * returned list will be present inside the JAXB object. +// * This is why there is not a <CODE>set</CODE> method for the oneOrManyOrAny property. +// * <p/> +// * <p/> +// * For example, to add a new item, do as follows: +// * <pre> +// * getOneOrManyOrAny().add(newItem); +// * </pre> +// * <p/> +// * <p/> +// * <p/> +// * Objects of the following type(s) are allowed in the list +// * {@link JAXBElement }{@code <}{@link ManyType }{@code >} +// * {@link Element } +// * {@link Object } +// * {@link JAXBElement }{@code <}{@link OneType }{@code >} +// */ + public List<Object> getAny() + { + if (any == null) + { + any = new ArrayList<Object>(); + } + return this.any; + } + + public List<OneType> getOneList() + { + if (oneList == null) + { + oneList = new ArrayList<OneType>(); + } + return this.oneList; + } + + public List<ManyType> getManyList() + { + if (manyList == null) + { + manyList = new ArrayList<ManyType>(); + } + return this.manyList; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ManyType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ManyType.java new file mode 100644 index 0000000..3a05650 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ManyType.java @@ -0,0 +1,104 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +import java.util.*; +//import javax.xml.bind.*; +//import javax.xml.bind.annotation.*; + +/** + * <p>Java class for manyType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="manyType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <choice maxOccurs="unbounded" minOccurs="0"> + * <element name="except" type="{urn:ietf:params:xml:ns:common-policy}exceptType"/> + * <any/> + * </choice> + * <attribute name="domain" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "manyType", propOrder = { +// "exceptOrAny" +// }) +//@XmlRootElement(name = "many", +// namespace = "urn:ietf:params:xml:ns:common-policy") +public class ManyType +{ + +// @XmlElementRef(name = "except", +// namespace = "urn:ietf:params:xml:ns:common-policy", +// type = JAXBElement.class) +// @XmlAnyElement(lax = true) + protected List<Object> exceptOrAny; + +// @XmlAttribute + protected String domain; + +// /** +// * Gets the value of the exceptOrAny property. +// * <p/> +// * <p/> +// * This accessor method returns a reference to the live list, +// * not a snapshot. Therefore any modification you make to the +// * returned list will be present inside the JAXB object. +// * This is why there is not a <CODE>set</CODE> method for the exceptOrAny property. +// * <p/> +// * <p/> +// * For example, to add a new item, do as follows: +// * <pre> +// * getExceptOrAny().add(newItem); +// * </pre> +// * <p/> +// * <p/> +// * <p/> +// * Objects of the following type(s) are allowed in the list +// * {@link Element } +// * {@link JAXBElement }{@code <}{@link ExceptType }{@code >} +// * {@link Object } +// */ + public List<Object> getExceptOrAny() + { + if (exceptOrAny == null) + { + exceptOrAny = new ArrayList<Object>(); + } + return this.exceptOrAny; + } + + /** + * Gets the value of the domain property. + * + * @return possible object is + * {@link String } + */ + public String getDomain() + { + return domain; + } + + /** + * Sets the value of the domain property. + * + * @param value allowed object is + * {@link String } + */ + public void setDomain(String value) + { + this.domain = value; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/OneType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/OneType.java new file mode 100644 index 0000000..e3ac678 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/OneType.java @@ -0,0 +1,91 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +import org.w3c.dom.*; + +/** + * <p>Java class for oneType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="oneType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <any/> + * </sequence> + * <attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}anyURI" /> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "oneType", propOrder = { +// "any" +// }) +//@XmlRootElement(name = "one", +// namespace = "urn:ietf:params:xml:ns:common-policy") +public class OneType +{ + +// @XmlAnyElement(lax = true) + protected Object any; + +// @XmlAttribute(required = true) + protected String id; + + /** + * Gets the value of the any property. + * + * @return possible object is + * {@link Element } + * {@link Object } + */ + public Object getAny() + { + return any; + } + + /** + * Sets the value of the any property. + * + * @param value allowed object is + * {@link Element } + * {@link Object } + */ + public void setAny(Object value) + { + this.any = value; + } + + /** + * Gets the value of the id property. + * + * @return possible object is + * {@link String } + */ + public String getId() + { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value allowed object is + * {@link String } + */ + public void setId(String value) + { + this.id = value; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/RuleType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/RuleType.java new file mode 100644 index 0000000..209d877 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/RuleType.java @@ -0,0 +1,141 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +/** + * <p>Java class for ruleType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="ruleType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element name="conditions" type="{urn:ietf:params:xml:ns:common-policy}conditionsType" minOccurs="0"/> + * <element name="actions" type="{urn:ietf:params:xml:ns:common-policy}extensibleType" minOccurs="0"/> + * <element name="transformations" type="{urn:ietf:params:xml:ns:common-policy}extensibleType" minOccurs="0"/> + * </sequence> + * <attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}ID" /> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "ruleType", propOrder = { +// "conditions", +// "actions", +// "transformations" +// }) +public class RuleType +{ + +// @XmlElement(namespace = "urn:ietf:params:xml:ns:common-policy") + protected ConditionsType conditions; + +// @XmlElement(namespace = "urn:ietf:params:xml:ns:common-policy") + protected ActionsType actions; + +// @XmlElement(namespace = "urn:ietf:params:xml:ns:common-policy") + protected TransfomationsType transformations; + +// @XmlAttribute(required = true) +// @XmlJavaTypeAdapter(CollapsedStringAdapter.class) +// @XmlID + protected String id; + + /** + * Gets the value of the conditions property. + * + * @return possible object is + * {@link ConditionsType } + */ + public ConditionsType getConditions() + { + return conditions; + } + + /** + * Sets the value of the conditions property. + * + * @param value allowed object is + * {@link ConditionsType } + */ + public void setConditions(ConditionsType value) + { + this.conditions = value; + } + + /** + * Gets the value of the actions property. + * + * @return possible object is + * {@link TransfomationsType } + */ + public ActionsType getActions() + { + return actions; + } + + /** + * Sets the value of the actions property. + * + * @param value allowed object is + * {@link TransfomationsType } + */ + public void setActions(ActionsType value) + { + this.actions = value; + } + + /** + * Gets the value of the transformations property. + * + * @return possible object is + * {@link TransfomationsType } + */ + public TransfomationsType getTransformations() + { + return transformations; + } + + /** + * Sets the value of the transformations property. + * + * @param value allowed object is + * {@link TransfomationsType } + */ + public void setTransformations(TransfomationsType value) + { + this.transformations = value; + } + + /** + * Gets the value of the id property. + * + * @return possible object is + * {@link String } + */ + public String getId() + { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value allowed object is + * {@link String } + */ + public void setId(String value) + { + this.id = value; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/RulesetType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/RulesetType.java new file mode 100644 index 0000000..b287ac3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/RulesetType.java @@ -0,0 +1,74 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +import java.util.*; +import java.util.List; +//import javax.xml.bind.annotation.*; + +/** + * <p>Java class for ruleset element declaration. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <element name="ruleset"> + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element name="rule" type="{urn:ietf:params:xml:ns:common-policy}ruleType" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * </element> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "", propOrder = { +// "rule" +// }) +//@XmlRootElement(name = "ruleset") +public class RulesetType +{ +// +// @XmlElement(namespace = "urn:ietf:params:xml:ns:common-policy", +// required = true) + protected List<RuleType> rule; + + /** + * Gets the value of the rule property. + * <p/> + * <p/> + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a <CODE>set</CODE> method for the rule property. + * <p/> + * <p/> + * For example, to add a new item, do as follows: + * <pre> + * getRule().add(newItem); + * </pre> + * <p/> + * <p/> + * <p/> + * Objects of the following type(s) are allowed in the list + * {@link RuleType } + */ + public List<RuleType> getRule() + { + if (rule == null) + { + rule = new ArrayList<RuleType>(); + } + return this.rule; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/SphereType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/SphereType.java new file mode 100644 index 0000000..1ed4cb7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/SphereType.java @@ -0,0 +1,55 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +/** + * <p>Java class for sphereType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="sphereType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="value" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "sphereType") +public class SphereType +{ + +// @XmlAttribute(required = true) + protected String value; + + /** + * Gets the value of the value property. + * + * @return possible object is + * {@link String } + */ + public String getValue() + { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value allowed object is + * {@link String } + */ + public void setValue(String value) + { + this.value = value; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/TransfomationsType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/TransfomationsType.java new file mode 100644 index 0000000..a5adc78 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/TransfomationsType.java @@ -0,0 +1,120 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.presrules.*; +import org.w3c.dom.*; + +import java.util.*; + +/** + * <p>Java class for extensibleType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="extensibleType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <any/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "transfomationsType", propOrder = { +// "servicePermission", +// "personPermission", +// "devicePermission", +// "any" +// }) +public class TransfomationsType +{ + +// @XmlElement(name = "provide-services", +// namespace = "urn:ietf:params:xml:ns:resource-lists", +// required = false) + protected ProvideServicePermission servicePermission; + +// @XmlElement(name = "provide-persons", +// namespace = "urn:ietf:params:xml:ns:resource-lists", +// required = false) + protected ProvidePersonPermission personPermission; + +// @XmlElement(name = "provide-devices", +// namespace = "urn:ietf:params:xml:ns:resource-lists", +// required = false) + protected ProvideDevicePermission devicePermission; + +// @XmlAnyElement(lax = true) + protected List<Object> any; + + public ProvideServicePermission getServicePermission() + { + return servicePermission; + } + + public void setServicePermission(ProvideServicePermission servicePermission) + { + this.servicePermission = servicePermission; + } + + public ProvidePersonPermission getPersonPermission() + { + return personPermission; + } + + public void setPersonPermission(ProvidePersonPermission personPermission) + { + this.personPermission = personPermission; + } + + public ProvideDevicePermission getDevicePermission() + { + return devicePermission; + } + + public void setDevicePermission(ProvideDevicePermission devicePermission) + { + this.devicePermission = devicePermission; + } + + /** + * Gets the value of the any property. + * <p/> + * <p/> + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a <CODE>set</CODE> method for the any property. + * <p/> + * <p/> + * For example, to add a new item, do as follows: + * <pre> + * getAny().add(newItem); + * </pre> + * <p/> + * <p/> + * <p/> + * Objects of the following type(s) are allowed in the list + * {@link Element } + * {@link Object } + */ + public List<Object> getAny() + { + if (any == null) + { + any = new ArrayList<Object>(); + } + return this.any; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ValidityType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ValidityType.java new file mode 100644 index 0000000..cdff1ce --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/ValidityType.java @@ -0,0 +1,75 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.commonpolicy; + +/** + * <p>Java class for validityType complex type. + * <p/> + * <p>The following schema fragment specifies the expected content contained within this class. + * <p/> + * <pre> + * <complexType name="validityType"> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence maxOccurs="unbounded"> + * <element name="from" type="{http://www.w3.org/2001/XMLSchema}dateTime"/> + * <element name="until" type="{http://www.w3.org/2001/XMLSchema}dateTime"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * </pre> + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "validityType", propOrder = { +// "fromAndUntil" +// }) +public class ValidityType +{ + +// @XmlElementRefs({ +// @XmlElementRef(name = "from", +// namespace = "urn:ietf:params:xml:ns:common-policy", +// type = JAXBElement.class), +// @XmlElementRef(name = "until", +// namespace = "urn:ietf:params:xml:ns:common-policy", +// type = JAXBElement.class) +// }) +// protected List<JAXBElement<XMLGregorianCalendar>> fromAndUntil; + +// /** +// * Gets the value of the fromAndUntil property. +// * <p/> +// * <p/> +// * This accessor method returns a reference to the live list, +// * not a snapshot. Therefore any modification you make to the +// * returned list will be present inside the JAXB object. +// * This is why there is not a <CODE>set</CODE> method for the fromAndUntil property. +// * <p/> +// * <p/> +// * For example, to add a new item, do as follows: +// * <pre> +// * getFromAndUntil().add(newItem); +// * </pre> +// * <p/> +// * <p/> +// * <p/> +// * Objects of the following type(s) are allowed in the list +// * {@link JAXBElement }{@code <}{@link XMLGregorianCalendar }{@code >} +// * {@link JAXBElement }{@code <}{@link XMLGregorianCalendar }{@code >} +// */ +// public List<JAXBElement<XMLGregorianCalendar>> getFromAndUntil() +// { +// if (fromAndUntil == null) +// { +// fromAndUntil = new ArrayList<JAXBElement<XMLGregorianCalendar>>(); +// } +// return this.fromAndUntil; +// } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/common-policy.xsd b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/common-policy.xsd new file mode 100644 index 0000000..bc84e4b --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/commonpolicy/common-policy.xsd @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema xmlns:cp="urn:ietf:params:xml:ns:common-policy" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + targetNamespace="urn:ietf:params:xml:ns:common-policy" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + <xs:element name="ruleset"> + <xs:complexType> + <xs:complexContent> + <xs:restriction base="xs:anyType"> + <xs:sequence> + <xs:element name="rule" type="cp:ruleType" minOccurs="0" + maxOccurs="unbounded"/> + </xs:sequence> + </xs:restriction> + </xs:complexContent> + </xs:complexType> + </xs:element> + <xs:complexType name="ruleType"> + <xs:complexContent> + <xs:restriction base="xs:anyType"> + <xs:sequence> + <xs:element name="conditions" type="cp:conditionsType" minOccurs="0"/> + <xs:element name="actions" type="cp:extensibleType" minOccurs="0"/> + <xs:element name="transformations" type="cp:extensibleType" minOccurs="0"/> + </xs:sequence> + <xs:attribute name="id" type="xs:ID" use="required"/> + </xs:restriction> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="conditionsType"> + <xs:complexContent> + <xs:restriction base="xs:anyType"> + <xs:choice maxOccurs="unbounded"> + <xs:element name="identity" type="cp:identityType" minOccurs="0"/> + <xs:element name="sphere" type="cp:sphereType" minOccurs="0"/> + <xs:element name="validity" type="cp:validityType" minOccurs="0"/> + <xs:any namespace="##other" processContents="lax" minOccurs="0" + maxOccurs="unbounded"/> + </xs:choice> + </xs:restriction> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="identityType"> + <xs:complexContent> + <xs:restriction base="xs:anyType"> + <xs:choice maxOccurs="unbounded"> + <xs:element name="one" type="cp:oneType"/> + <xs:element name="many" type="cp:manyType"/> + <xs:any namespace="##other" processContents="lax"/> + </xs:choice> + </xs:restriction> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="oneType"> + <xs:complexContent> + <xs:restriction base="xs:anyType"> + <xs:sequence> + <xs:any namespace="##other" processContents="lax" minOccurs="0"/> + </xs:sequence> + <xs:attribute name="id" type="xs:anyURI" use="required"/> + </xs:restriction> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="manyType"> + <xs:complexContent> + <xs:restriction base="xs:anyType"> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="except" type="cp:exceptType"/> + <xs:any namespace="##other" processContents="lax" minOccurs="0"/> + </xs:choice> + <xs:attribute name="domain" type="xs:string" use="optional"/> + </xs:restriction> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="exceptType"> + <xs:attribute name="domain" type="xs:string" use="optional"/> + <xs:attribute name="id" type="xs:anyURI" use="optional"/> + </xs:complexType> + <xs:complexType name="sphereType"> + <xs:complexContent> + <xs:restriction base="xs:anyType"> + <xs:attribute name="value" type="xs:string" use="required"/> + </xs:restriction> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="validityType"> + <xs:complexContent> + <xs:restriction base="xs:anyType"> + <xs:sequence maxOccurs="unbounded"> + <xs:element name="from" type="xs:dateTime"/> + <xs:element name="until" type="xs:dateTime"/> + </xs:sequence> + </xs:restriction> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="extensibleType"> + <xs:complexContent> + <xs:restriction base="xs:anyType"> + <xs:sequence> + <xs:any namespace="##other" processContents="lax" minOccurs="0" + maxOccurs="unbounded"/> + </xs:sequence> + </xs:restriction> + </xs:complexContent> + </xs:complexType> +</xs:schema> diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/ContentType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/ContentType.java new file mode 100644 index 0000000..172d3d3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/ContentType.java @@ -0,0 +1,173 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent; + +import java.util.*; +import javax.xml.namespace.*; + +import org.w3c.dom.*; + +/** + * The XCAP content element. + * <p/> + * Compliant with Presence Content XDM Specification v1.0 + * + * @author Grigorii Balutsel + */ +public class ContentType +{ + /** + * The data element. + */ + private DataType data; + + /** + * The mime-type element. + */ + private MimeType mimeType; + + /** + * The encoding element. + */ + private EncodingType encoding; + + /** + * The list of description elements. + */ + private List<DescriptionType> description; + + /** + * The list of any elements. + */ + private List<Element> any; + + /** + * The map of any attributes. + */ + private Map<QName, String> anyAttributes = new HashMap<QName, String>(); + + /** + * Gets the value of the mimeType property. + * + * @return the mimeType property. + */ + public MimeType getMimeType() + { + return mimeType; + } + + /** + * Sets the value of the mimeType property. + * + * @param mimeType the mimeType to set. + */ + public void setMimeType(MimeType mimeType) + { + this.mimeType = mimeType; + } + + + /** + * Gets the value of the encoding property. + * + * @return the encoding property. + */ + public EncodingType getEncoding() + { + return encoding; + } + + /** + * Sets the value of the encoding property. + * + * @param encoding the encoding to set. + */ + public void setEncoding(EncodingType encoding) + { + this.encoding = encoding; + } + + + /** + * Gets the value of the description property. + * + * @return the description property. + */ + public List<DescriptionType> getDescription() + { + if (description == null) + { + description = new ArrayList<DescriptionType>(); + } + return this.description; + } + + + /** + * Gets the value of the data property. + * + * @return the data property. + */ + public DataType getData() + { + return data; + } + + /** + * Sets the value of the data property. + * + * @param data the data to set. + */ + public void setData(DataType data) + { + this.data = data; + } + + /** + * Gets the value of the any property. + * + * @return the any property. + */ + public List<Element> getAny() + { + if (any == null) + { + any = new ArrayList<Element>(); + } + return this.any; + } + + /** + * Sets the value of the any property. + * + * @param any the any to set. + */ + public void setAny(List<Element> any) + { + this.any = any; + } + + /** + * Gets the value of the anyAttributes property. + * + * @return the anyAttributes property. + */ + public Map<QName, String> getAnyAttributes() + { + return anyAttributes; + } + + /** + * Sets the value of the anyAttributes property. + * + * @param anyAttributes the anyAttributes to set. + */ + public void setAnyAttributes(Map<QName, String> anyAttributes) + { + this.anyAttributes = anyAttributes; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/DataType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/DataType.java new file mode 100644 index 0000000..be02ba1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/DataType.java @@ -0,0 +1,60 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent; + +import javax.xml.namespace.*; +import java.util.*; + +/** + * The PRES-CONTENT data element. + * <p/> + * Compliant with Presence Content XDM Specification v1.0 + * + * @author Grigorii Balutsel + */ +public class DataType +{ + /** + * The element value. + */ + protected String value; + + /** + * The map of any attributes. + */ + private Map<QName, String> anyAttributes = new HashMap<QName, String>(); + + /** + * Gets the value of the value property. + * + * @return the value property. + */ + public String getValue() + { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value the value to set. + */ + public void setValue(String value) + { + this.value = value; + } + + /** + * Gets the value of the anyAttributes property. + * + * @return the anyAttributes property. + */ + public Map<QName, String> getAnyAttributes() + { + return anyAttributes; + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/DescriptionType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/DescriptionType.java new file mode 100644 index 0000000..70fb631 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/DescriptionType.java @@ -0,0 +1,114 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent; + +import javax.xml.namespace.*; +import java.util.*; + +/** + * The PRES-CONTENT description element. + * <p/> + * Compliant with Presence Content XDM Specification v1.0 + * + * @author Grigorii Balutsel + */ +public class DescriptionType +{ + /** + * The element value. + */ + private String value; + + /** + * The lang attribute. + */ + private String lang; + + /** + * The map of any attributes. + */ + private Map<QName, String> anyAttributes = new HashMap<QName, String>(); + + /** + * Creates description. + */ + public DescriptionType() + { + } + + /** + * Creates description with value. + * + * @param value the value property. + */ + public DescriptionType(String value) + { + this(value, null); + } + + /** + * Creates description with value and lang properties. + * + * @param value the value property. + * @param lang the lang property. + */ + public DescriptionType(String value, String lang) + { + this.value = value; + this.lang = lang; + } + + /** + * Gets the value of the value property. + * + * @return the value property. + */ + public String getValue() + { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value the value to set. + */ + public void setValue(String value) + { + this.value = value; + } + + /** + * Gets the value of the lang property. + * + * @return the lang property. + */ + public String getLang() + { + return lang; + } + + /** + * Sets the value of the lang property. + * + * @param lang the lang to set. + */ + public void setLang(String lang) + { + this.lang = lang; + } + + /** + * Gets the value of the anyAttributes property. + * + * @return the anyAttributes property. + */ + public Map<QName, String> getAnyAttributes() + { + return anyAttributes; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/EncodingType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/EncodingType.java new file mode 100644 index 0000000..f79093a --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/EncodingType.java @@ -0,0 +1,60 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent; + +import javax.xml.namespace.*; +import java.util.*; + +/** + * The PRES-CONTENT encoding element. + * <p/> + * Compliant with Presence Content XDM Specification v1.0 + * + * @author Grigorii Balutsel + */ +public class EncodingType +{ + /** + * The element value. + */ + protected String value; + + /** + * The map of any attributes. + */ + private Map<QName, String> anyAttributes = new HashMap<QName, String>(); + + /** + * Gets the value of the value property. + * + * @return the value property. + */ + public String getValue() + { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value the value to set. + */ + public void setValue(String value) + { + this.value = value; + } + + /** + * Gets the value of the anyAttributes property. + * + * @return the anyAttributes property. + */ + public Map<QName, String> getAnyAttributes() + { + return anyAttributes; + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/MimeType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/MimeType.java new file mode 100644 index 0000000..86bd358 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/MimeType.java @@ -0,0 +1,60 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent; + +import javax.xml.namespace.*; +import java.util.*; + +/** + * The PRES-CONTENT mime-type element. + * <p/> + * Compliant with Presence Content XDM Specification v1.0 + * + * @author Grigorii Balutsel + */ +public class MimeType +{ + /** + * The element value. + */ + protected String value; + + /** + * The map of any attributes. + */ + private Map<QName, String> anyAttributes = new HashMap<QName, String>(); + + /** + * Gets the value of the value property. + * + * @return the value property. + */ + public String getValue() + { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value the value to set. + */ + public void setValue(String value) + { + this.value = value; + } + + /** + * Gets the value of the anyAttributes property. + * + * @return the anyAttributes property. + */ + public Map<QName, String> getAnyAttributes() + { + return anyAttributes; + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/PresContentParser.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/PresContentParser.java new file mode 100644 index 0000000..ffd24db --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/PresContentParser.java @@ -0,0 +1,476 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.*; +import static net.java.sip.communicator.impl.protocol.sip.xcap.model.XmlUtils.*; +import static net.java.sip.communicator.impl.protocol.sip.xcap.model.StringUtils.*; + +import static javax.xml.XMLConstants.*; + +import org.w3c.dom.*; + +import javax.xml.namespace.*; +import java.util.*; + +/** + * Utility class that helps to converts pres-content xml to the object model and + * object model to the pres-content xml. + * + * @author Grigorii Balutsel + */ +public class PresContentParser +{ + private static String NAMESPACE = "urn:oma:xml:prs:pres-content"; + + private static String CONTENT_ELEMENT = "content"; + + private static String MIMETYPE_ELEMENT = "mime-type"; + + private static String ENCODING_ELEMENT = "encoding"; + + private static String DESCRIPTION_ELEMENT = "description"; + + private static String DESCRIPTION_LANG_ATTR = "lang"; + + private static String DATA_ELEMENT = "data"; + + /** + * Creates xcap-caps object from the element. + * + * @param xml the XML to analyze. + * @return the xcap-caps object. + * @throws ParsingException if there is some error during parsing. + */ + public static ContentType fromXml(String xml) + throws ParsingException + { + if (isNullOrEmpty(xml)) + { + throw new IllegalArgumentException("XML cannot be null or empty"); + } + try + { + ContentType content = new ContentType(); + Document document = XmlUtils.createDocument(xml); + Element contentElement = document.getDocumentElement(); + if (CONTENT_ELEMENT.equals(contentElement.getLocalName()) && + !NAMESPACE.equals(contentElement.getNamespaceURI())) + { + throw new Exception( + "Document doesn't contain content element"); + } + // Process attributes + NamedNodeMap attributes = contentElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("content element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (NAMESPACE.equals(namespaceUri)) + { + throw new Exception("content element is invalid"); + } + QName qName = new QName(namespaceUri, + attribute.getLocalName(), + attribute.getPrefix() == null ? "" : + attribute.getPrefix()); + content.getAnyAttributes().put(qName, attribute.getValue()); + } + // Process elements + NodeList childNodes = contentElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String namespaceUri = getNamespaceUri(element); + if (namespaceUri == null) + { + throw new Exception("content element is invalid"); + } + String localName = node.getLocalName(); + if (NAMESPACE.equals(namespaceUri)) + { + // data + if (DATA_ELEMENT.equals(localName)) + { + content.setData(dataFromElement(element)); + } + // mime-type + else if (MIMETYPE_ELEMENT.equals(localName)) + { + content.setMimeType(mimeTypeFromElement(element)); + } + // encoding + else if (ENCODING_ELEMENT.equals(localName)) + { + content.setEncoding(encodingFromElement(element)); + } + // descritpion + else if (DESCRIPTION_ELEMENT.equals(localName)) + { + content.getDescription() + .add(descriptionFromElement(element)); + } + else + { + throw new Exception("content element is invalid"); + } + } + else + { + // any + content.getAny().add(element); + } + } + return content; + } + catch (Exception ex) + { + throw new ParsingException(ex); + } + } + + /** + * Creates XML from the pres-content element. + * + * @param content the pres-content to analyze. + * @return the pres-content xml. + * @throws ParsingException if there is some error during parsing. + */ + public static String toXml(ContentType content) + throws ParsingException + { + if (content == null) + { + throw new IllegalArgumentException("pres-content cannot be null"); + } + try + { + Document document = createDocument(); + Element presContentElement = + document.createElementNS(NAMESPACE, CONTENT_ELEMENT); + if (content.getData() != null) + { + presContentElement.appendChild(elementFromValue( + document, + DATA_ELEMENT, + content.getData().getValue(), + content.getData().getAnyAttributes())); + } + if (content.getEncoding() != null) + { + presContentElement.appendChild(elementFromValue( + document, + ENCODING_ELEMENT, + content.getEncoding().getValue(), + content.getEncoding().getAnyAttributes())); + } + if (content.getMimeType() != null) + { + presContentElement.appendChild(elementFromValue( + document, + MIMETYPE_ELEMENT, + content.getMimeType().getValue(), + content.getMimeType().getAnyAttributes())); + } + for (DescriptionType description : content.getDescription()) + { + presContentElement.appendChild( + elementFromDescription(document, description)); + } + processAnyAttributes(presContentElement, + content.getAnyAttributes()); + processAny(presContentElement, content.getAny()); + document.appendChild(presContentElement); + return createXml(document); + } + catch (Exception ex) + { + throw new ParsingException(ex); + } + } + + /** + * Creates display-name element from the value and attributes. + * + * @param document the xml document. + * @param nodeName the local node name. + * @param value the xml node value. + * @param anyAttributes the map of any attributes + * @return the xml element. + */ + private static Element elementFromValue( + Document document, String nodeName, String value, + Map<QName, String> anyAttributes) + { + Element element = document.createElementNS(NAMESPACE, nodeName); + if (value != null) + { + element.setTextContent(value); + } + processAnyAttributes(element, anyAttributes); + return element; + } + + /** + * Creates ddescriptionelement from the object. + * + * @param document the xml document. + * @param description the description to analyze. + * @return the description element. + * @throws Exception if there is some error during creating. + */ + private static Element elementFromDescription( + Document document, + DescriptionType description) + throws Exception + { + Element element = document.createElementNS(NAMESPACE, + DESCRIPTION_ELEMENT); + if (description.getLang() != null) + { + element.setAttribute( + XML_NS_PREFIX + ":" + DESCRIPTION_LANG_ATTR, + description.getLang()); + } + if (description.getValue() != null) + { + element.setTextContent(description.getValue()); + } + processAnyAttributes(element, description.getAnyAttributes()); + return element; + } + + /** + * Creates data object from the element. + * + * @param element the element to analyze. + * @return the display-name object. + * @throws Exception if there is some error during parsing. + */ + private static DataType dataFromElement(Element element) + throws Exception + { + DataType result = new DataType(); + if (!DATA_ELEMENT.equals(element.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("data element is invalid"); + } + // Process attributes + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("data element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (NAMESPACE.equals(namespaceUri)) + { + throw new Exception("data element is invalid"); + } + QName qName = new QName(namespaceUri, + attribute.getLocalName(), + attribute.getPrefix() == null ? "" : + attribute.getPrefix()); + result.getAnyAttributes().put(qName, attribute.getValue()); + } + // Process elements + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) + { + throw new Exception("data element is invalid"); + } + } + result.setValue(element.getTextContent()); + return result; + } + + /** + * Creates data object from the element. + * + * @param element the element to analyze. + * @return the data object. + * @throws Exception if there is some error during parsing. + */ + private static EncodingType encodingFromElement(Element element) + throws Exception + { + EncodingType result = new EncodingType(); + if (!ENCODING_ELEMENT.equals(element.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("encoding element is invalid"); + } + // Process attributes + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("encoding element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (NAMESPACE.equals(namespaceUri)) + { + throw new Exception("encoding element is invalid"); + } + QName qName = new QName(namespaceUri, + attribute.getLocalName(), + attribute.getPrefix() == null ? "" : + attribute.getPrefix()); + result.getAnyAttributes().put(qName, attribute.getValue()); + } + // Process elements + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) + { + throw new Exception("encoding element is invalid"); + } + } + result.setValue(element.getTextContent()); + return result; + } + + /** + * Creates mime-type object from the element. + * + * @param element the element to analyze. + * @return the mime-type object. + * @throws Exception if there is some error during parsing. + */ + private static MimeType mimeTypeFromElement(Element element) + throws Exception + { + MimeType result = new MimeType(); + if (!MIMETYPE_ELEMENT.equals(element.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("mime-type element is invalid"); + } + // Process attributes + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("mime-type element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (NAMESPACE.equals(namespaceUri)) + { + throw new Exception("mime-type element is invalid"); + } + } + // Process elements + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) + { + throw new Exception("encoding element is invalid"); + } + } + result.setValue(element.getTextContent()); + return result; + } + + /** + * Creates description object from the element. + * + * @param element the element to analyze. + * @return the description object. + * @throws Exception if there is some error during parsing. + */ + private static DescriptionType descriptionFromElement(Element element) + throws Exception + { + DescriptionType result = new DescriptionType(); + if (!DESCRIPTION_ELEMENT.equals(element.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("description element is invalid"); + } + // Process attributes + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("description element is invalid"); + } + if (DESCRIPTION_LANG_ATTR.equals(attribute.getLocalName()) && + XML_NS_URI.equals(namespaceUri)) + { + result.setLang(attribute.getValue()); + continue; + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (NAMESPACE.equals(namespaceUri)) + { + throw new Exception("description element is invalid"); + } + QName qName = new QName(namespaceUri, + attribute.getLocalName(), + attribute.getPrefix() == null ? "" : + attribute.getPrefix()); + result.getAnyAttributes().put(qName, attribute.getValue()); + } + // Process elements + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) + { + throw new Exception("description element is invalid"); + } + } + result.setValue(element.getTextContent()); + return result; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/pres-content.xsd b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/pres-content.xsd new file mode 100644 index 0000000..11d6017 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/prescontent/pres-content.xsd @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + +Schema for the Presence Content Application Usage + version - 1.0 + date - 23 Dec 2008 + +FILE INFORMATION + + OMA Permanent Document + File: OMA-SUP-XSD_prs_presContent-V1_0-20081223-C + Type: Text - Schema Description + + Public Reachable Information + Path: http://www.openmobilealliance.org/Technical/schemas.aspx + Name: prs_presContent-v1_0.xsd +NORMATIVE INFORMATION + + Information about this file can be found in the specification + + OMA-TS-Presence-SIMPLE_Content_XDM-V1_0 + + available at http://www.openmobilealliance.org/ + + Send comments to technical-comments@mail.openmobilealliance.org + +LEGAL DISCLAIMER + + Use of this document is subject to all of the terms and conditions + of the Use Agreement located at + http://www.openmobilealliance.org/UseAgreement.html + + You may use this document or any part of the document for internal + or educational purposes only, provided you do not modify, edit or + take out of context the information in this document in any manner. + Information contained in this document may be used, at your sole + risk, for any purposes. + + You may not use this document in any other manner without the prior + written permission of the Open Mobile Alliance. The Open Mobile + Alliance authorizes you to copy this document, provided that you + retain all copyright and other proprietary notices contained in the + original materials on any copies of the materials and that you + comply strictly with these terms. This copyright permission does + not constitute an endorsement of the products or services. The + Open Mobile Alliance assumes no responsibility for errors or + omissions in this document. + + Each Open Mobile Alliance member has agreed to use reasonable + endeavors to inform the Open Mobile Alliance in a timely manner of + Essential IPR as it becomes aware that the Essential IPR is related + to the prepared or published specification. However, the members + do not have an obligation to conduct IPR searches. The declared + Essential IPR is publicly available to members and non-members of + the Open Mobile Alliance and may be found on the "OMA IPR + Declarations" list at http://www.openmobilealliance.org/ipr.html. + The Open Mobile Alliance has not conducted an independent IPR review + of this document and the information contained herein, and makes no + representations or warranties regarding third party IPR, including + without limitation patents, copyrights or trade secret rights. This + document may contain inventions for which you must obtain licenses + from third parties before making, using or selling the inventions. + Defined terms above are set forth in the schedule to the Open Mobile + Alliance Application Form. + + NO REPRESENTATIONS OR WARRANTIES (WHETHER EXPRESS OR IMPLIED) ARE + MADE BY THE OPEN MOBILE ALLIANCE OR ANY OPEN MOBILE ALLIANCE MEMBER + OR ITS AFFILIATES REGARDING ANY OF THE IPR'S REPRESENTED ON THE "OMA + IPR DECLARATIONS" LIST, INCLUDING, BUT NOT LIMITED TO THE ACCURACY, + COMPLETENESS, VALIDITY OR RELEVANCE OF THE INFORMATION OR WHETHER OR + NOT SUCH RIGHTS ARE ESSENTIAL OR NON-ESSENTIAL. + + THE OPEN MOBILE ALLIANCE IS NOT LIABLE FOR AND HEREBY DISCLAIMS ANY + DIRECT, INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL, CONSEQUENTIAL, OR + EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE USE OF + DOCUMENTS AND THE INFORMATION CONTAINED IN THE DOCUMENTS. + + Copyright 2008 Open Mobile Alliance Ltd. All Rights Reserved. + Used with the permission of the Open Mobile Alliance Ltd. under the + terms set forth above. + +--> + +<xs:schema targetNamespace="urn:oma:xml:prs:pres-content" + xmlns="urn:oma:xml:prs:pres-content" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" + attributeFormDefault="unqualified"> + + <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/> + <xs:element name="content"> + <xs:complexType> + <xs:sequence> + <xs:element name="mime-type" minOccurs="0"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:anyAttribute processContents="lax"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + + <xs:element name="encoding" minOccurs="0"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:anyAttribute processContents="lax"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + + <xs:element name="description" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute ref="xml:lang"/> + <xs:anyAttribute processContents="lax"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + + <xs:element name="data"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:anyAttribute processContents="lax"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + + <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:anyAttribute processContents="lax"/> + </xs:complexType> + </xs:element> + +</xs:schema> + + diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvideAllAttributes.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvideAllAttributes.java new file mode 100644 index 0000000..ded8676 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvideAllAttributes.java @@ -0,0 +1,22 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.presrules; + +/** + * Grants access to all presence attributes in all of the person, device, and + * tuple elements that are present in thedocument + * <p/> + * Compliant with rfc5025. + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "") +//@XmlRootElement(name = "provide-all-attributes") +public class ProvideAllAttributes +{ +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvideDevicePermission.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvideDevicePermission.java new file mode 100644 index 0000000..b3f9363 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvideDevicePermission.java @@ -0,0 +1,78 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.presrules; + +import java.util.*; + +/** + * Allows a watcher to see "device" information present in the presence + * document. + * <p/> + * Compliant with rfc5025. + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "provideDevicePermission", propOrder = { +// "allDevices", +// "deviceIDOrOccurrenceIdOrClazz" +// }) +//@XmlRootElement(name = "providedevices", +// namespace = "urn:ietf:params:xml:ns:pres-rules") +public class ProvideDevicePermission +{ +// +// @XmlElement(name = "all-devices", +// namespace = "urn:ietf:params:xml:ns:pres-rules") + protected AllDevices allDevices; +// @XmlElementRefs({ +// @XmlElementRef(name = "deviceID", namespace = "urn:ietf:params:xml:ns:pres-rules", type = JAXBElement.class), +// @XmlElementRef(name = "occurrence-id", namespace = "urn:ietf:params:xml:ns:pres-rules", type = JAXBElement.class), +// @XmlElementRef(name = "class", namespace = "urn:ietf:params:xml:ns:pres-rules", type = JAXBElement.class) + + // }) + +// @XmlAnyElement(lax = true) + protected List<Object> deviceIDOrOccurrenceIdOrClazz; + + /** + * Gets the value of the allDevices property. + * + * @return possible object is + * {@link AllDevices } + */ + public AllDevices getAllDevices() + { + return allDevices; + } + + /** + * Sets the value of the allDevices property. + * + * @param value allowed object is + * {@link AllDevices } + */ + public void setAllDevices(AllDevices value) + { + this.allDevices = value; + } + + public List<Object> getDeviceIDOrOccurrenceIdOrClazz() + { + if (deviceIDOrOccurrenceIdOrClazz == null) + { + deviceIDOrOccurrenceIdOrClazz = new ArrayList<Object>(); + } + return this.deviceIDOrOccurrenceIdOrClazz; + } + + + + public static class AllDevices + { + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvidePersonPermission.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvidePersonPermission.java new file mode 100644 index 0000000..bf4c673 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvidePersonPermission.java @@ -0,0 +1,82 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.presrules; + +import java.util.*; + + +/** + * Allows a watcher to see the "person" information present in the presence + * document. + * <p/> + * Compliant with rfc5025. + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "providePersonPermission", propOrder = { +// "allPersons", +// "occurrenceIdOrClazzOrAny" +// }) +//@XmlRootElement(name = "provide-persons", +// namespace = "urn:ietf:params:xml:ns:pres-rules") +public class ProvidePersonPermission +{ +// +// @XmlElement(name = "all-persons", +// namespace = "urn:ietf:params:xml:ns:pres-rules") + protected AllPersons allPersons; +// @XmlElementRefs({ +// @XmlElementRef(name = "occurrence-id", namespace = "urn:ietf:params:xml:ns:pres-rules", type = JAXBElement.class), + + // @XmlElementRef(name = "class", namespace = "urn:ietf:params:xml:ns:pres-rules", type = JAXBElement.class) + // }) +// @XmlAnyElement(lax = true) + protected List<Object> occurrenceIdOrClazzOrAny; + + /** + * Gets the value of the allPersons property. + * + * @return possible object is + * {@link AllPersons } + */ + public AllPersons getAllPersons() + { + return allPersons; + } + + /** + * Sets the value of the allPersons property. + * + * @param value allowed object is + * {@link AllPersons } + */ + public void setAllPersons(AllPersons value) + { + this.allPersons = value; + } + + + public List<Object> getOccurrenceIdOrClazzOrAny() + { + if (occurrenceIdOrClazzOrAny == null) + { + occurrenceIdOrClazzOrAny = new ArrayList<Object>(); + } + return this.occurrenceIdOrClazzOrAny; + } + + +// +// @XmlAccessorType(XmlAccessType.FIELD) +// @XmlType(name = "") + public static class AllPersons + { + + + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvideServicePermission.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvideServicePermission.java new file mode 100644 index 0000000..0dbbb93 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/ProvideServicePermission.java @@ -0,0 +1,86 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.presrules; + +import java.util.*; + +/** + * Allows a watcher to see service information present in "tuple" elements in + * the presence document the subscription authorization decision that the server + * should make. + * <p/> + * Compliant with rfc5025. + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "provideServicePermission", propOrder = { +// "allServices", +// "serviceUriOrServiceUriSchemeOrOccurrenceId" +// }) +//@XmlRootElement(name = "provide-services", +// namespace = "urn:ietf:params:xml:ns:pres-rules") +public class ProvideServicePermission +{ +// +// @XmlElement(name = "all-services", +// namespace = "urn:ietf:params:xml:ns:pres-rules") + protected AllServices allServices; +// @XmlElementRefs({ +// @XmlElementRef(name = "service-uri", namespace = "urn:ietf:params:xml:ns:pres-rules", type = JAXBElement.class), +// @XmlElementRef(name = "service-uri-scheme", namespace = "urn:ietf:params:xml:ns:pres-rules", type = JAXBElement.class), +// @XmlElementRef(name = "occurrence-id", namespace = "urn:ietf:params:xml:ns:pres-rules", type = JAXBElement.class), +// @XmlElementRef(name = "class", namespace = "urn:ietf:params:xml:ns:pres-rules", type = JAXBElement.class) + + // }) + +// @XmlAnyElement(lax = true) + protected List<Object> serviceUriOrServiceUriSchemeOrOccurrenceId; + + /** + * Gets the value of the allServices property. + * + * @return possible object is + * {@link AllServices } + */ + public AllServices getAllServices() + { + return allServices; + } + + /** + * Sets the value of the allServices property. + * + * @param value allowed object is + * {@link AllServices } + */ + public void setAllServices(AllServices value) + { + this.allServices = value; + } + + + public List<Object> getServiceUriOrServiceUriSchemeOrOccurrenceId() + { + if (serviceUriOrServiceUriSchemeOrOccurrenceId == null) + { + serviceUriOrServiceUriSchemeOrOccurrenceId = + new ArrayList<Object>(); + } + return this.serviceUriOrServiceUriSchemeOrOccurrenceId; + } + + + +// @XmlAccessorType(XmlAccessType.FIELD) +// @XmlType(name = "") + public static class AllServices + { + + + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/SubHandlingType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/SubHandlingType.java new file mode 100644 index 0000000..f898ea2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/SubHandlingType.java @@ -0,0 +1,70 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.presrules; + +/** + * Specifies the subscription authorization decision that the server should + * make. + * <p/> + * Compliant with rfc5025. + * + * @author Grigorii Balutsel + */ +//@XmlEnum +public enum SubHandlingType +{ + /** + * This action tells the server to reject the subscription, placing it in + * the "terminated" state. + */ +// @XmlEnumValue("block") + Block("block"), + /** + * This action tells the server to place the subscription in the "pending" + * state, and await input from the presentity to determine how to proceed. + */ +// @XmlEnumValue("confirm") + Confirm("confirm"), + /** + * This action tells the server to place the subscription into the "active" + * state, and to produce a presence document that indicates that the + * presentity is unavailable. + */ +// @XmlEnumValue("polite-block") + PoliteBlock("polite-block"), + /** + * This action tells the server to place the subscription into the "active" + * state. + */ +// @XmlEnumValue("allow") + Allow("allow"); + + /** + * Current enum value. + */ + private final String value; + + /** + * Creates enum whith the specified value. + * + * @param value the value to set. + */ + SubHandlingType(String value) + { + this.value = value; + } + + /** + * Gets the value. + * + * @return the value + */ + public String value() + { + return value; + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/UnknownBooleanPermission.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/UnknownBooleanPermission.java new file mode 100644 index 0000000..543f75c --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/UnknownBooleanPermission.java @@ -0,0 +1,100 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.presrules; + +/** + * Indicates that the unknown presence attribute with the given name and + * namespace should be included in the document. + * <p/> + * Compliant with rfc5025 + * + * @author Grigorii Balutsel + */ +//@XmlAccessorType(XmlAccessType.FIELD) +//@XmlType(name = "unknownBooleanPermission", propOrder = { +// "value" +// }) +public class UnknownBooleanPermission +{ + /** + * The value. + */ +// @XmlValue + protected boolean value; + + /** + * Presence name. + */ +// @XmlAttribute(required = true) + protected String name; + + /** + * Namespace URI. + */ +// @XmlAttribute(required = true) + protected String ns; + + /** + * Gets the value of the value property. + */ + public boolean isValue() + { + return value; + } + + /** + * Sets the value of the value property. + */ + public void setValue(boolean value) + { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return possible object is + * {@link String } + */ + public String getName() + { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value allowed object is + * {@link String } + */ + public void setName(String value) + { + this.name = value; + } + + /** + * Gets the value of the ns property. + * + * @return possible object is + * {@link String } + */ + public String getNs() + { + return ns; + } + + /** + * Sets the value of the ns property. + * + * @param value allowed object is + * {@link String } + */ + public void setNs(String value) + { + this.ns = value; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/pres-rules.xsd b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/pres-rules.xsd new file mode 100644 index 0000000..84ab3c0 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/presrules/pres-rules.xsd @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema targetNamespace="urn:ietf:params:xml:ns:pres-rules" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:cr="urn:ietf:params:xml:ns:common-policy" + xmlns:pr="urn:ietf:params:xml:ns:pres-rules" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + <xs:import namespace="urn:ietf:params:xml:ns:common-policy"/> + <xs:simpleType name="booleanPermission"> + <xs:restriction base="xs:boolean"/> + </xs:simpleType> + <xs:element name="service-uri-scheme" type="xs:token"/> + <xs:element name="class" type="xs:token"/> + + <xs:element name="occurrence-id" type="xs:token"/> + <xs:element name="service-uri" type="xs:anyURI"/> + <xs:complexType name="provideServicePermission"> + <xs:choice> + <xs:element name="all-services"> + <xs:complexType/> + </xs:element> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:choice> + <xs:element ref="pr:service-uri"/> + <xs:element ref="pr:service-uri-scheme"/> + <xs:element ref="pr:occurrence-id"/> + <xs:element ref="pr:class"/> + <xs:any namespace="##other" processContents="lax"/> + </xs:choice> + </xs:sequence> + </xs:choice> + </xs:complexType> + <xs:element name="provide-services" + type="pr:provideServicePermission"/> + <xs:element name="deviceID" type="xs:anyURI"/> + <xs:complexType name="provideDevicePermission"> + <xs:choice> + <xs:element name="all-devices"> + <xs:complexType/> + </xs:element> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:choice> + <xs:element ref="pr:deviceID"/> + <xs:element ref="pr:occurrence-id"/> + <xs:element ref="pr:class"/> + <xs:any namespace="##other" processContents="lax"/> + </xs:choice> + </xs:sequence> + </xs:choice> + </xs:complexType> + <xs:element name="provide-devices" + type="pr:provideDevicePermission"/> + <xs:complexType name="providePersonPermission"> + <xs:choice> + <xs:element name="all-persons"> + <xs:complexType/> + </xs:element> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:choice> + <xs:element ref="pr:occurrence-id"/> + <xs:element ref="pr:class"/> + <xs:any namespace="##other" processContents="lax"/> + </xs:choice> + </xs:sequence> + </xs:choice> + </xs:complexType> + <xs:element name="provide-persons" + type="pr:providePersonPermission"/> + <xs:element name="provide-activities" + type="pr:booleanPermission"/> + <xs:element name="provide-class" + type="pr:booleanPermission"/> + <xs:element name="provide-deviceID" + type="pr:booleanPermission"/> + <xs:element name="provide-mood" + type="pr:booleanPermission"/> + <xs:element name="provide-place-is" + type="pr:booleanPermission"/> + <xs:element name="provide-place-type" + type="pr:booleanPermission"/> + <xs:element name="provide-privacy" + type="pr:booleanPermission"/> + <xs:element name="provide-relationship" + type="pr:booleanPermission"/> + <xs:element name="provide-status-icon" + type="pr:booleanPermission"/> + <xs:element name="provide-sphere" + type="pr:booleanPermission"/> + <xs:element name="provide-time-offset" + type="pr:booleanPermission"/> + <xs:element name="provide-user-input"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="false"/> + <xs:enumeration value="bare"/> + <xs:enumeration value="thresholds"/> + <xs:enumeration value="full"/> + </xs:restriction> + </xs:simpleType> + </xs:element> + <xs:element name="provide-note" type="pr:booleanPermission"/> + <xs:element name="sub-handling"> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="block"/> + <xs:enumeration value="confirm"/> + <xs:enumeration value="polite-block"/> + <xs:enumeration value="allow"/> + </xs:restriction> + </xs:simpleType> + </xs:element> + <xs:complexType name="unknownBooleanPermission"> + <xs:simpleContent> + <xs:extension base="pr:booleanPermission"> + <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="ns" type="xs:string" use="required"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + <xs:element name="provide-unknown-attribute" + type="pr:unknownBooleanPermission"/> + <xs:element name="provide-all-attributes"> + <xs:complexType/> + </xs:element> +</xs:schema> diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/DisplayNameType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/DisplayNameType.java new file mode 100644 index 0000000..4a7cc12 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/DisplayNameType.java @@ -0,0 +1,86 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists; + +/** + * The XCAP display-name element. + * <p/> + * Compliant with rfc4825, rfc4826 + * + * @author Grigorii Balutsel + */ +public class DisplayNameType +{ + /** + * The element value. + */ + private String value; + + /** + * The lang attribute. + */ + private String lang; + + /** + * Creates display-name. + */ + public DisplayNameType() + { + } + + /** + * Creates display-name with value and lang properties. + * + * @param value the value property. + * @param lang the lang property. + */ + public DisplayNameType(String value, String lang) + { + this.value = value; + this.lang = lang; + } + + /** + * Gets the value of the value property. + * + * @return the value property. + */ + public String getValue() + { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value the value to set. + */ + public void setValue(String value) + { + this.value = value; + } + + /** + * Gets the value of the lang property. + * + * @return the lang property. + */ + public String getLang() + { + return lang; + } + + /** + * Sets the value of the lang property. + * + * @param lang the lang to set. + */ + public void setLang(String lang) + { + this.lang = lang; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/EntryRefType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/EntryRefType.java new file mode 100644 index 0000000..b33be81 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/EntryRefType.java @@ -0,0 +1,129 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists; + +import java.util.*; +import javax.xml.namespace.*; + +import org.w3c.dom.*; + +/** + * The XCAP entry-ref element. + * <p/> + * Compliant with rfc4825, rfc4826 + * + * @author Grigorii Balutsel + */ +public class EntryRefType +{ + /** + * The ref attribute. + */ + private String ref; + + /** + * The display-name element. + */ + private DisplayNameType displayName; + + /** + * The list of any elements. + */ + private List<Element> any; + + /** + * The map of any attributes. + */ + private Map<QName, String> anyAttributes = new HashMap<QName, String>(); + + /** + * Creates the entry-ref element + */ + EntryRefType() + { + } + + /** + * Creates the entry-ref element with the ref attribute. + * + * @param ref the ref attribute. + * @throws IllegalArgumentException if ref attribute is null or empty. + */ + public EntryRefType(String ref) + { + if (ref == null || ref.trim().length() == 0) + { + throw new IllegalArgumentException("The ref attribute cannot be " + + "null or empry"); + } + this.ref = ref; + } + + /** + * Gets the value of the ref property. + * + * @return the ref property. + */ + public String getRef() + { + return ref; + } + + /** + * Sets the value of the ref property. + * + * @param ref the ref to set. + */ + public void setRef(String ref) + { + this.ref = ref; + } + + /** + * Gets the value of the displayName property. + * + * @return the displayName property. + */ + public DisplayNameType getDisplayName() + { + return displayName; + } + + /** + * Sets the value of the displayName property. + * + * @param displayName the displayName to set. + */ + public void setDisplayName(DisplayNameType displayName) + { + this.displayName = displayName; + } + + /** + * Gets the value of the any property. + * + * @return the any property. + */ + public List<Element> getAny() + { + if (any == null) + { + any = new ArrayList<Element>(); + } + return this.any; + } + + /** + * Gets the value of the anyAttributes property. + * + * @return the anyAttributes property. + */ + public Map<QName, String> getAnyAttributes() + { + return anyAttributes; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/EntryType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/EntryType.java new file mode 100644 index 0000000..75318a8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/EntryType.java @@ -0,0 +1,149 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists; + +import java.util.*; +import javax.xml.namespace.*; + +import org.w3c.dom.*; + +/** + * The XCAP entry element. + * <p/> + * Compliant with rfc4825, rfc4826 + * + * @author Grigorii Balutsel + */ +public class EntryType +{ + /** + * The uri attribute. + */ + private String uri; + + /** + * The display-name element. + */ + private DisplayNameType displayName; + + /** + * The list of any elements. + */ + private List<Element> any; + + /** + * The map of any attributes. + */ + private Map<QName, String> anyAttributes = new HashMap<QName, String>(); + + /** + * Create the entry element. + */ + EntryType() + { + } + + /** + * Create the entry element with the uri attribute. + * + * @param uri the uri attribute. + * @throws IllegalArgumentException if uri attribute is null or empty. + */ + public EntryType(String uri) + { + if (uri == null || uri.trim().length() == 0) + { + throw new IllegalArgumentException("The uri attribute cannot be " + + "null or empry"); + } + this.uri = uri; + } + + /** + * Gets the value of the uri property. + * + * @return the uri property. + */ + public String getUri() + { + return uri; + } + + /** + * Sets the value of the uri property. + * + * @param uri the uri to set. + */ + public void setUri(String uri) + { + this.uri = uri; + } + + /** + * Gets the value of the displayName property. + * + * @return the displayName property. + */ + public DisplayNameType getDisplayName() + { + return displayName; + } + + /** + * Sets the value of the displayName property. + * + * @param displayName the displayName to set. + */ + public void setDisplayName(DisplayNameType displayName) + { + this.displayName = displayName; + } + + /** + * Gets the value of the any property. + * + * @return the any property. + */ + public List<Element> getAny() + { + if (any == null) + { + any = new ArrayList<Element>(); + } + return this.any; + } + + /** + * Sets the value of the any property. + * + * @param any the any to set. + */ + public void setAny(List<Element> any) + { + this.any = any; + } + + /** + * Gets the value of the anyAttributes property. + * + * @return the anyAttributes property. + */ + public Map<QName, String> getAnyAttributes() + { + return anyAttributes; + } + + /** + * Sets the value of the anyAttributes property. + * + * @param anyAttributes the anyAttributes to set. + */ + public void setAnyAttributes(Map<QName, String> anyAttributes) + { + this.anyAttributes = anyAttributes; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ExternalType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ExternalType.java new file mode 100644 index 0000000..1bd72e6 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ExternalType.java @@ -0,0 +1,106 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists; + +import java.util.*; +import javax.xml.namespace.*; + +import org.w3c.dom.*; + +/** + * The XCAP external element. + * <p/> + * Compliant with rfc4825, rfc4826 + * + * @author Grigorii Balutsel + */ +public class ExternalType +{ + /** + * The danchor attribute. + */ + protected String anchor; + + /** + * The display-name element. + */ + private DisplayNameType displayName; + + /** + * The list of any elements. + */ + private List<Element> any; + + /** + * The map of any attributes. + */ + private Map<QName, String> anyAttributes = new HashMap<QName, String>(); + + /** + * Gets the value of the anchor property. + * + * @return the anchor property. + */ + public String getAnchor() + { + return anchor; + } + + /** + * Sets the value of the anchor property. + * + * @param anchor the anchor to set. + */ + public void setAnchor(String anchor) + { + this.anchor = anchor; + } + + /** + * Gets the value of the displayName property. + * + * @return the displayName property. + */ + public DisplayNameType getDisplayName() + { + return displayName; + } + + /** + * Sets the value of the displayName property. + * + * @param displayName the displayName to set. + */ + public void setDisplayName(DisplayNameType displayName) + { + this.displayName = displayName; + } + + /** + * Gets the value of the any property. + * + * @return the any property. + */ + public List<Element> getAny() + { + if (any == null) + { + any = new ArrayList<Element>(); + } + return this.any; + } + + /** + * Gets the value of the anyAttributes property. + * + * @return the anyAttributes property. + */ + public Map<QName, String> getAnyAttributes() + { + return anyAttributes; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ListType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ListType.java new file mode 100644 index 0000000..3caa9ab --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ListType.java @@ -0,0 +1,202 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists; + +import org.w3c.dom.*; + +import javax.xml.namespace.*; +import java.util.*; + +/** + * The XCAP list element. + * <p/> + * Compliant with rfc4825, rfc4826 + * + * @author Grigorii Balutsel + */ +public class ListType +{ + /** + * The name attribute. + */ + protected String name; + + /** + * The display-name element. + */ + protected DisplayNameType displayName; + + /** + * The list of entry elements. + */ + protected List<EntryType> entries; + + /** + * The list of external elements. + */ + protected List<ExternalType> externals; + + /** + * The list of list elements. + */ + protected List<ListType> lists; + + /** + * The list of entry-ref elements. + */ + protected List<EntryRefType> entryRefs; + + /** + * The list of any elements. + */ + private List<Element> any; + + /** + * The map of any attributes. + */ + private Map<QName, String> anyAttributes = new HashMap<QName, String>(); + + /** + * Gets the value of the displayName property. + * + * @return the displayName property. + */ + public DisplayNameType getDisplayName() + { + return displayName; + } + + /** + * Sets the value of the displayName property. + * + * @param displayName the displayName to set. + */ + public void setDisplayName(DisplayNameType displayName) + { + this.displayName = displayName; + } + + /** + * Gets the value of the name property. + * + * @return the name property. + */ + public String getName() + { + return name; + } + + /** + * Sets the value of the name property. + * + * @param name the name to set. + */ + public void setName(String name) + { + this.name = name; + } + + /** + * Gets the value of the entries property. + * + * @return the entries property. + */ + public List<EntryType> getEntries() + { + if (entries == null) + { + entries = new ArrayList<EntryType>(); + } + return entries; + } + + /** + * Gets the value of the externals property. + * + * @return the externals property. + */ + public List<ExternalType> getExternals() + { + if (externals == null) + { + externals = new ArrayList<ExternalType>(); + } + return externals; + } + + /** + * Gets the value of the lists property. + * + * @return the lists property. + */ + public List<ListType> getLists() + { + if (lists == null) + { + lists = new ArrayList<ListType>(); + } + return lists; + } + + /** + * Gets the value of the entryRefs property. + * + * @return the entryRefs property. + */ + public List<EntryRefType> getEntryRefs() + { + if (entryRefs == null) + { + entryRefs = new ArrayList<EntryRefType>(); + } + return entryRefs; + } + + /** + * Gets the value of the any property. + * + * @return the any property. + */ + public List<Element> getAny() + { + if (any == null) + { + any = new ArrayList<Element>(); + } + return this.any; + } + + /** + * Sets the value of the any property. + * + * @param any the any to set. + */ + public void setAny(List<Element> any) + { + this.any = any; + } + + /** + * Gets the value of the anyAttributes property. + * + * @return the anyAttributes property. + */ + public Map<QName, String> getAnyAttributes() + { + return anyAttributes; + } + + /** + * Sets the value of the anyAttributes property. + * + * @param anyAttributes the anyAttributes to set. + */ + public void setAnyAttributes(Map<QName, String> anyAttributes) + { + this.anyAttributes = anyAttributes; + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ResourceListsParser.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ResourceListsParser.java new file mode 100644 index 0000000..6e5ca3e --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ResourceListsParser.java @@ -0,0 +1,716 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.*; +import static net.java.sip.communicator.impl.protocol.sip.xcap.model.StringUtils.*; +import static net.java.sip.communicator.impl.protocol.sip.xcap.model.XmlUtils.*; +import org.w3c.dom.*; + +import static javax.xml.XMLConstants.*; +import javax.xml.namespace.*; + +/** + * Utility class that helps to converts resource-lists xml to the object model + * and object model to the resource-lists xml. + * + * @author Grigorii Balutsel + */ +public final class ResourceListsParser +{ + private static String NAMESPACE = "urn:ietf:params:xml:ns:resource-lists"; + + private static String RESOURCE_LISTS_ELEMENT = "resource-lists"; + + private static String LIST_ELEMENT = "list"; + + private static String LIST_NAME_ATTR = "name"; + + private static String ENTRY_ELEMENT = "entry"; + + private static String ENTRY_URI_ATTR = "uri"; + + private static String ENTRYREF_ELEMENT = "entry-ref"; + + private static String ENTRYREF_REF_ATTR = "ref"; + + private static String EXTERNAL_ELEMENT = "external"; + + private static String EXTERNAL_ANCHOR_ATTR = "anchor"; + + private static String DISPALY_NAME_ELEMENT = "display-name"; + + private static String DISPALY_NAME_LANG_ATTR = "lang"; + + private ResourceListsParser() + { + } + + /** + * Creates resource-lists object from the element. + * + * @param xml the XML to analyze. + * @return the resource-lists object. + * @throws ParsingException if there is some error during parsing. + */ + public static ResourceListsType fromXml(String xml) + throws ParsingException + { + if (isNullOrEmpty(xml)) + { + throw new IllegalArgumentException("XML cannot be null or empty"); + } + try + { + ResourceListsType resourceLists = new ResourceListsType(); + Document document = createDocument(xml); + Element resourceListsElement = document.getDocumentElement(); + String localName = resourceListsElement.getLocalName(); + if (RESOURCE_LISTS_ELEMENT.equals(localName) && + !NAMESPACE.equals(resourceListsElement.getNamespaceURI())) + { + throw new Exception("Document doesn't contain resource-lists " + + "element"); + } + // Process attributes + NamedNodeMap attributes = resourceListsElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("resource-lists element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + throw new Exception("resource-lists element is invalid"); + } + // Process elements + NodeList childNodes = resourceListsElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element listElement = (Element) node; + resourceLists.getList().add(listFromElement(listElement)); + } + return resourceLists; + } + catch (Exception ex) + { + throw new ParsingException(ex); + } + } + + /** + * Creates XML from the resource-lists element. + * + * @param resourceLists the resource-lists to analyze. + * @return the resource-lists xml. + * @throws ParsingException if there is some error during parsing. + */ + public static String toXml(ResourceListsType resourceLists) + throws ParsingException + { + if (resourceLists == null) + { + throw new IllegalArgumentException("resource-lists cannot be null"); + } + try + { + Document document = createDocument(); + Element resourceListsElement = + document.createElementNS(NAMESPACE, RESOURCE_LISTS_ELEMENT); + for (ListType list : resourceLists.getList()) + { + resourceListsElement + .appendChild(elementFromList(document, list)); + } + document.appendChild(resourceListsElement); + return createXml(document); + } + catch (Exception ex) + { + throw new ParsingException(ex); + } + } + + /** + * Creates list object from the element. + * + * @param listElement the element to analyze. + * @return the list object. + * @throws Exception if there is some error during parsing. + */ + private static ListType listFromElement(Element listElement) + throws Exception + { + ListType list = new ListType(); + if (!LIST_ELEMENT.equals(listElement.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(listElement))) + { + throw new Exception("list element is invalid"); + } + // Process attributes + NamedNodeMap attributes = listElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("list element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (NAMESPACE.equals(namespaceUri)) + { + if (LIST_NAME_ATTR.equals(attribute.getLocalName())) + { + list.setName(attribute.getValue()); + continue; + } + else + { + throw new Exception("list element is invalid"); + } + } + QName qName = new QName(namespaceUri, + attribute.getLocalName(), + attribute.getPrefix() == null ? "" : attribute.getPrefix()); + list.getAnyAttributes().put(qName, attribute.getValue()); + } + // Process elements + NodeList childNodes = listElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String localName = element.getLocalName(); + String namespaceUri = getNamespaceUri(element); + if (NAMESPACE.equals(namespaceUri)) + { + // display-name + if (DISPALY_NAME_ELEMENT.equals(localName)) + { + list.setDisplayName(displayNameFromElement(element)); + } + // entry + else if (ENTRY_ELEMENT.equals(localName)) + { + list.getEntries().add(entryFromElement(element)); + } + // entry-ref + else if (ENTRYREF_ELEMENT.equals(localName)) + { + list.getEntryRefs().add(entryRefFromElement(element)); + } + // list + else if (LIST_ELEMENT.equals(localName)) + { + list.getLists().add(listFromElement(element)); + } + // extenal + else if (EXTERNAL_ELEMENT.equals(localName)) + { + list.getExternals().add(externalFromElement(element)); + } + else + { + throw new Exception("list element is invalid"); + } + } + else + { + // any + list.getAny().add(element); + } + } + return list; + } + + /** + * Creates list element from the object. + * + * @param document the xml document. + * @param list the list to analyze. + * @return the list element. + * @throws Exception if there is some error during creating. + */ + private static Element elementFromList(Document document, ListType list) + throws Exception + { + Element listElement = document.createElementNS(NAMESPACE, LIST_ELEMENT); + if (list.getName() != null) + { + listElement.setAttribute(LIST_NAME_ATTR, list.getName()); + } + // display-name + if (list.getDisplayName() != null) + { + listElement.appendChild(elementFromDisplayName(document, + list.getDisplayName())); + } + // entry + for (EntryType entry : list.getEntries()) + { + listElement.appendChild(elementFromEntry(document, entry)); + } + // entry-ref + for (EntryRefType entryRef : list.getEntryRefs()) + { + listElement.appendChild(elementFromEntryRef(document, entryRef)); + } + // list + for (ListType subList : list.getLists()) + { + listElement.appendChild(elementFromList(document, subList)); + } + // external + for (ExternalType external : list.getExternals()) + { + listElement.appendChild(elementFromExternal(document, external)); + } + processAnyAttributes(listElement, list.getAnyAttributes()); + processAny(listElement, list.getAny()); + return listElement; + } + + /** + * Creates entry element from the object. + * + * @param document the xml document. + * @param entry the entry to analyze. + * @return the entry element. + * @throws Exception if there is some error during creating. + */ + private static Element elementFromEntry(Document document, EntryType entry) + throws Exception + { + Element entryElement = document.createElementNS(NAMESPACE, + ENTRY_ELEMENT); + if (isNullOrEmpty(entry.getUri())) + { + throw new Exception("entry uri attribute is missed"); + } + entryElement.setAttribute(ENTRY_URI_ATTR, entry.getUri()); + if (entry.getDisplayName() != null) + { + entryElement.appendChild(elementFromDisplayName(document, + entry.getDisplayName())); + } + processAnyAttributes(entryElement, entry.getAnyAttributes()); + processAny(entryElement, entry.getAny()); + return entryElement; + } + + /** + * Creates entryRef element from the object. + * + * @param document the xml document. + * @param entryRef the entry to analyze. + * @return the entryRef element. + * @throws Exception if there is some error during creating. + */ + private static Element elementFromEntryRef( + Document document, + EntryRefType entryRef) + throws Exception + { + Element entryRefElement = document.createElementNS(NAMESPACE, + ENTRYREF_ELEMENT); + if (isNullOrEmpty(entryRef.getRef())) + { + throw new Exception("entry-ref ref attribute is missed"); + } + entryRefElement.setAttribute(ENTRYREF_REF_ATTR, entryRef.getRef()); + if (entryRef.getDisplayName() != null) + { + entryRefElement.appendChild(elementFromDisplayName(document, + entryRef.getDisplayName())); + } + processAnyAttributes(entryRefElement, entryRef.getAnyAttributes()); + processAny(entryRefElement, entryRef.getAny()); + return entryRefElement; + } + + /** + * Creates external element from the object. + * + * @param document the xml document. + * @param external the entry to analyze. + * @return the external element. + * @throws Exception if there is some error during creating. + */ + private static Element elementFromExternal( + Document document, + ExternalType external) + throws Exception + { + Element externalElement = document.createElementNS(NAMESPACE, + EXTERNAL_ELEMENT); + if (!isNullOrEmpty(external.getAnchor())) + { + externalElement.setAttribute(EXTERNAL_ANCHOR_ATTR, + external.getAnchor()); + } + if (external.getDisplayName() != null) + { + externalElement.appendChild(elementFromDisplayName(document, + external.getDisplayName())); + } + processAnyAttributes(externalElement, external.getAnyAttributes()); + processAny(externalElement, external.getAny()); + return externalElement; + } + + /** + * Creates display-name element from the object. + * + * @param document the xml document. + * @param displayName the display-name to analyze. + * @return the external element. + * @throws Exception if there is some error during creating. + */ + private static Element elementFromDisplayName( + Document document, + DisplayNameType displayName) + throws Exception + { + Element displayNameElement = document.createElementNS(NAMESPACE, + DISPALY_NAME_ELEMENT); + if (displayName.getLang() != null) + { + displayNameElement.setAttribute( + XML_NS_PREFIX + ":" + DISPALY_NAME_LANG_ATTR, + displayName.getLang()); + } + if (displayName.getValue() != null) + { + displayNameElement.setTextContent(displayName.getValue()); + } + return displayNameElement; + } + + /** + * Creates entry object from the element. + * + * @param entryElement the element to analyze. + * @return the entry object. + * @throws Exception if there is some error during parsing. + */ + private static EntryType entryFromElement(Element entryElement) + throws Exception + { + EntryType entry = new EntryType(); + if (!ENTRY_ELEMENT.equals(entryElement.getNodeName()) || + !NAMESPACE.equals(getNamespaceUri(entryElement))) + { + throw new Exception("entry element is invalid"); + } + String uri = null; + // Process attributes + NamedNodeMap attributes = entryElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("entry element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (NAMESPACE.equals(namespaceUri)) + { + if (ENTRY_URI_ATTR.equals(attribute.getLocalName())) + { + uri = attribute.getValue(); + continue; + } + else + { + throw new Exception("entry element is invalid"); + } + } + QName qName = new QName(namespaceUri, + attribute.getLocalName(), + attribute.getPrefix() == null ? "" : attribute.getPrefix()); + entry.getAnyAttributes().put(qName, attribute.getValue()); + } + if (uri == null) + { + throw new Exception("entry uri attribute is missed"); + } + entry.setUri(uri); + // Process elements + NodeList childNodes = entryElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String namespaceUri = getNamespaceUri(element); + if (namespaceUri == null) + { + throw new Exception("entry element is invalid"); + } + if (NAMESPACE.equals(namespaceUri)) + { + // display-name + if (DISPALY_NAME_ELEMENT.equals(element.getLocalName())) + { + entry.setDisplayName(displayNameFromElement(element)); + continue; + } + else + { + throw new Exception("entry element is invalid"); + } + } + // any + entry.getAny().add(element); + } + return entry; + } + + /** + * Creates entry-ref object from the element. + * + * @param entryRefElement the element to analyze. + * @return the entry-ref object. + * @throws Exception if there is some error during parsing. + */ + private static EntryRefType entryRefFromElement(Element entryRefElement) + throws Exception + { + EntryRefType entryRef = new EntryRefType(); + if (!ENTRYREF_ELEMENT.equals(entryRefElement.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(entryRefElement))) + { + throw new Exception("entry-ref element is invalid"); + } + String ref = null; + // Process attributes + NamedNodeMap attributes = entryRefElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("entry-ref element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (NAMESPACE.equals(namespaceUri)) + { + if (ENTRYREF_REF_ATTR.equals(attribute.getLocalName())) + { + ref = attribute.getValue(); + continue; + } + else + { + throw new Exception("entry-ref element is invalid"); + } + } + QName qName = new QName(attribute.getNamespaceURI(), + attribute.getName(), + attribute.getPrefix() == null ? "" : attribute.getPrefix()); + entryRef.getAnyAttributes().put(qName, attribute.getValue()); + } + if (ref == null) + { + throw new Exception("entry-ref ref attribute is missed"); + } + entryRef.setRef(ref); + // Process elements + NodeList childNodes = entryRefElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String namespaceUri = getNamespaceUri(element); + if (namespaceUri == null) + { + throw new Exception("entry-ref element is invalid"); + } + if (NAMESPACE.equals(namespaceUri)) + { + // display-name + if (DISPALY_NAME_ELEMENT.equals(element.getLocalName())) + { + entryRef.setDisplayName(displayNameFromElement(element)); + continue; + } + else + { + throw new Exception("entry-ref element is invalid"); + } + } + // any + entryRef.getAny().add(element); + } + return entryRef; + } + + /** + * Creates external object from the element. + * + * @param entryElement the element to analyze. + * @return the external object. + * @throws Exception if there is some error during parsing. + */ + private static ExternalType externalFromElement(Element entryElement) + throws Exception + { + ExternalType external = new ExternalType(); + if (!EXTERNAL_ELEMENT.equals(entryElement.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(entryElement))) + { + throw new Exception("external element is invalid"); + } + + // Process attributes + NamedNodeMap attributes = entryElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("external element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (NAMESPACE.equals(namespaceUri)) + { + if (EXTERNAL_ANCHOR_ATTR.equals(attribute.getLocalName())) + { + external.setAnchor(attribute.getValue()); + continue; + } + else + { + throw new Exception("external element is invalid"); + } + } + QName qName = new QName(attribute.getNamespaceURI(), + attribute.getName(), + attribute.getPrefix() == null ? "" : attribute.getPrefix()); + external.getAnyAttributes().put(qName, attribute.getValue()); + } + // Process elements + NodeList childNodes = entryElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String namespaceUri = getNamespaceUri(element); + if (namespaceUri == null) + { + throw new Exception("external element is invalid"); + } + if (NAMESPACE.equals(namespaceUri)) + { + // display-name + if (DISPALY_NAME_ELEMENT.equals(element.getLocalName())) + { + external.setDisplayName(displayNameFromElement(element)); + continue; + } + else + { + throw new Exception("external element is invalid"); + } + } + // any + external.getAny().add(element); + } + return external; + } + + /** + * Creates display-name object from the element. + * + * @param displayNameElement the element to analyze. + * @return the display-name object. + * @throws Exception if there is some error during parsing. + */ + private static DisplayNameType displayNameFromElement( + Element displayNameElement) throws Exception + { + DisplayNameType displayName = new DisplayNameType(); + if (!DISPALY_NAME_ELEMENT.equals(displayNameElement.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(displayNameElement))) + { + throw new Exception("display-name element is invalid"); + } + // Process attributes + NamedNodeMap attributes = displayNameElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("display-name element is invalid"); + } + if (DISPALY_NAME_LANG_ATTR.equals(attribute.getLocalName()) && + XML_NS_URI.equals(namespaceUri)) + { + displayName.setLang(attribute.getValue()); + } + else if (!isStandartXmlNamespace(namespaceUri)) + { + throw new Exception("display-name element is invalid"); + } + } + // Process elements + NodeList childNodes = displayNameElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) + { + throw new Exception("display-name element is invalid"); + } + } + displayName.setValue(displayNameElement.getTextContent()); + return displayName; + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ResourceListsType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ResourceListsType.java new file mode 100644 index 0000000..fb37854 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/ResourceListsType.java @@ -0,0 +1,38 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists; + +import java.util.*; + +/** + * The XCAP resource-lists element. + * <p/> + * Compliant with rfc4825, rfc4826 + * + * @author Grigorii Balutsel + */ +public class ResourceListsType +{ + /** + * The list of the list elements. + */ + private List<ListType> list; + + /** + * Gets the value of the list property. + * + * @return the list property. + */ + public List<ListType> getList() + { + if (list == null) + { + list = new ArrayList<ListType>(); + } + return this.list; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/resource-lists.xsd b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/resource-lists.xsd new file mode 100644 index 0000000..064918c --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/resourcelists/resource-lists.xsd @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema targetNamespace="urn:ietf:params:xml:ns:resource-lists" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns="urn:ietf:params:xml:ns:resource-lists" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + <xs:import namespace="http://www.w3.org/XML/1998/namespace" + schemaLocation="http://www.w3.org/2001/xml.xsd"/> + <xs:complexType name="listType"> + <xs:sequence> + <xs:element name="display-name" type="display-nameType" + minOccurs="0"/> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:choice> + <xs:element name="list"> + <xs:complexType> + <xs:complexContent> + <xs:extension base="listType"/> + </xs:complexContent> + </xs:complexType> + </xs:element> + <xs:element name="external" type="externalType"/> + <xs:element name="entry" type="entryType"/> + <xs:element name="entry-ref" type="entry-refType"/> + </xs:choice> + </xs:sequence> + <xs:any namespace="##other" processContents="lax" minOccurs="0" + maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="optional"/> + <xs:anyAttribute namespace="##other" processContents="lax"/> + </xs:complexType> + <xs:complexType name="entryType"> + <xs:sequence> + <xs:element name="display-name" minOccurs="0"> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="display-nameType"/> + </xs:simpleContent> + </xs:complexType> + </xs:element> + <xs:any namespace="##other" processContents="lax" minOccurs="0" + maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="uri" type="xs:anyURI" use="required"/> + <xs:anyAttribute namespace="##other" processContents="lax"/> + </xs:complexType> + <xs:complexType name="entry-refType"> + <xs:sequence> + <xs:element name="display-name" type="display-nameType" + minOccurs="0"/> + <xs:any namespace="##other" processContents="lax" minOccurs="0" + maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="ref" type="xs:anyURI" use="required"/> + <xs:anyAttribute namespace="##other" processContents="lax"/> + </xs:complexType> + <xs:complexType name="externalType"> + <xs:sequence> + <xs:element name="display-name" type="display-nameType" + minOccurs="0"/> + <xs:any namespace="##other" processContents="lax" minOccurs="0" + maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="anchor" type="xs:anyURI"/> + <xs:anyAttribute namespace="##other" processContents="lax"/> + </xs:complexType> + <xs:element name="resource-lists"> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:element name="list" type="listType"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:complexType name="display-nameType"> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute ref="xml:lang"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> +</xs:schema> diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/AuidsType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/AuidsType.java new file mode 100644 index 0000000..237118e --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/AuidsType.java @@ -0,0 +1,38 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps; + +import java.util.*; + +/** + * The XCAP-CAPS auids element. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class AuidsType +{ + /** + * The list of the auid elements. + */ + private List<String> auid; + + /** + * Gets the value of the auid property. + * + * @return the auid property. + */ + public List<String> getAuid() + { + if (auid == null) + { + auid = new ArrayList<String>(); + } + return this.auid; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/ExtensionsType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/ExtensionsType.java new file mode 100644 index 0000000..78238c2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/ExtensionsType.java @@ -0,0 +1,38 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps; + +import java.util.*; + +/** + * The XCAP-CAPS extensions element. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class ExtensionsType +{ + /** + * The list of the extension elements. + */ + private List<String> extension; + + /** + * Gets the value of the extension property. + * + * @return the extension property. + */ + public List<String> getExtension() + { + if (extension == null) + { + extension = new ArrayList<String>(); + } + return this.extension; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/NamespacesType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/NamespacesType.java new file mode 100644 index 0000000..2791460 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/NamespacesType.java @@ -0,0 +1,38 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps; + +import java.util.*; + +/** + * The XCAP-CAPS namespaces element. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class NamespacesType +{ + /** + * The list of the namespace elements. + */ + protected List<String> namespace; + + /** + * Gets the value of the auid property. + * + * @return the namespace property. + */ + public List<String> getNamespace() + { + if (namespace == null) + { + namespace = new ArrayList<String>(); + } + return this.namespace; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/XCapCapsParser.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/XCapCapsParser.java new file mode 100644 index 0000000..c8e3439 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/XCapCapsParser.java @@ -0,0 +1,324 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps; + +import static net.java.sip.communicator.impl.protocol.sip.xcap.model.StringUtils.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.*; +import static net.java.sip.communicator.impl.protocol.sip.xcap.model.XmlUtils.*; +import org.w3c.dom.*; + +/** + * Utility class that helps to converts xcap-caps xml to the object model and + * object model to the xcap-caps xml. + * + * @author Grigorii Balutsel + */ +public final class XCapCapsParser +{ + private static final String NAMESPACE = "urn:ietf:params:xml:ns:xcap-caps"; + + private static String XCAPCAPS_ELEMENT = "xcap-caps"; + + private static String AUIDS_ELEMENT = "auids"; + + private static String AUID_ELEMENT = "auid"; + + private static String NAMESPACES_ELEMENT = "namespaces"; + + private static String NAMESPACE_ELEMENT = "namespace"; + + private static String EXTENSIONS_ELEMENT = "extensions"; + + private static String EXTENSION_ELEMENT = "extension"; + + /** + * Creates xcap-caps object from the element. + * + * @param xml the XML to analyze. + * @return the xcap-caps object. + * @throws ParsingException if there is some error during parsing. + */ + public static XCapCapsType fromXml(String xml) + throws ParsingException + { + if (isNullOrEmpty(xml)) + { + throw new IllegalArgumentException("XML cannot be null or empty"); + } + try + { + XCapCapsType xCapCaps = new XCapCapsType(); + Document document = XmlUtils.createDocument(xml); + Element xCapCapsElement = document.getDocumentElement(); + if (XCAPCAPS_ELEMENT.equals(xCapCapsElement.getLocalName()) && + !NAMESPACE.equals(xCapCapsElement.getNamespaceURI())) + { + throw new Exception( + "Document doesn't contain xcap-caps element"); + } + boolean auidsFound = false; + boolean namespacesFound = false; + // Process attributes + NamedNodeMap attributes = xCapCapsElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("xcap-caps element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + throw new Exception("xcap-caps element is invalid"); + } + // Process elements + NodeList childNodes = xCapCapsElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String namespaceUri = getNamespaceUri(element); + if (namespaceUri == null) + { + throw new Exception("xcap-caps element is invalid"); + } + String localName = node.getLocalName(); + + if (NAMESPACE.equals(namespaceUri)) + { + // auids + if (AUIDS_ELEMENT.equals(localName)) + { + xCapCaps.setAuids(auidsFromElement(element)); + auidsFound = true; + } + // namspaces + else if (NAMESPACES_ELEMENT.equals(localName)) + { + xCapCaps.setNamespaces(namespacesFromElement(element)); + namespacesFound = true; + } + // extensions + else if (EXTENSIONS_ELEMENT.equals(localName)) + { + xCapCaps.setExtensions(extensionsFromElement(element)); + } + else + { + throw new Exception("xcap-caps element is invalid"); + } + } + else + { + // any + xCapCaps.getAny().add(element); + } + } + if (!auidsFound) + { + throw new ParsingException("xcap-caps auids element is missed"); + } + if (!namespacesFound) + { + throw new ParsingException( + "xcap-caps namespaces element is missed"); + } + return xCapCaps; + } + catch (Exception ex) + { + throw new ParsingException(ex); + } + } + + /** + * Creates auids object from the element. + * + * @param auidsElement the element to analyze. + * @return the auids object. + * @throws Exception if there is some error during parsing. + */ + private static AuidsType auidsFromElement( + Element auidsElement) throws Exception + { + AuidsType auidsType = new AuidsType(); + if (!AUIDS_ELEMENT.equals(auidsElement.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(auidsElement))) + { + throw new Exception("auids element is invalid"); + } + // Process attributes + NamedNodeMap attributes = auidsElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("auids element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + throw new Exception("auids element is invalid"); + } + // Process elements + NodeList childNodes = auidsElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String namespaceUri = getNamespaceUri(element); + if (namespaceUri == null) + { + throw new Exception("auids element is invalid"); + } + if (NAMESPACE.equals(namespaceUri) && + AUID_ELEMENT.equals(element.getLocalName())) + { + auidsType.getAuid().add(element.getTextContent()); + } + else + { + throw new Exception("auids element is invalid"); + } + } + return auidsType; + } + + /** + * Creates namespaces object from the element. + * + * @param namespacesElement the element to analyze. + * @return the namespaces object. + * @throws Exception if there is some error during parsing. + */ + private static NamespacesType namespacesFromElement( + Element namespacesElement) throws Exception + { + NamespacesType namespaces = new NamespacesType(); + if (!NAMESPACES_ELEMENT.equals(namespacesElement.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(namespacesElement))) + { + throw new Exception("namespaces element is invalid"); + } + // Process attributes + NamedNodeMap attributes = namespacesElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("namespaces element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + throw new Exception("namespaces element is invalid"); + } + // Process elements + NodeList childNodes = namespacesElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String namespaceUri = getNamespaceUri(element); + if (namespaceUri == null) + { + throw new Exception("namespaces element is invalid"); + } + if (NAMESPACE.equals(namespaceUri) && + NAMESPACE_ELEMENT.equals(element.getLocalName())) + { + namespaces.getNamespace().add(element.getTextContent()); + } + else + { + throw new Exception("namespaces element is invalid"); + } + } + return namespaces; + } + + /** + * Creates extensions object from the element. + * + * @param extensionsElement the element to analyze. + * @return the namespaces object. + * @throws Exception if there is some error during parsing. + */ + private static ExtensionsType extensionsFromElement( + Element extensionsElement) throws Exception + { + ExtensionsType extensions = new ExtensionsType(); + if (!EXTENSIONS_ELEMENT.equals(extensionsElement.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(extensionsElement))) + { + throw new Exception("extensions element is invalid"); + } + // Process attributes + NamedNodeMap attributes = extensionsElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("extensions element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + throw new Exception("extensions element is invalid"); + } + // Process elements + NodeList childNodes = extensionsElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String namespaceUri = getNamespaceUri(element); + if (namespaceUri == null) + { + throw new Exception("extensions element is invalid"); + } + if (NAMESPACE.equals(namespaceUri) && + EXTENSION_ELEMENT.equals(element.getLocalName())) + { + extensions.getExtension().add(element.getTextContent()); + } + else + { + throw new Exception("extensions element is invalid"); + } + } + return extensions; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/XCapCapsType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/XCapCapsType.java new file mode 100644 index 0000000..f435ccd --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/XCapCapsType.java @@ -0,0 +1,112 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps; + +import java.util.*; +import org.w3c.dom.*; + +/** + * Contains the capabilities of an XCAP server. + * + * @author Grigorii Balutsel + */ +public class XCapCapsType +{ + /** + * The auids elemet. + */ + protected AuidsType auids; + + /** + * The extensions elemet. + */ + protected ExtensionsType extensions; + + /** + * The namespaces elemet. + */ + protected NamespacesType namespaces; + + /** + * The list of any elements. + */ + protected List<Element> any; + + /** + * Gets the value of the auids property. + * + * @return the auids property. + */ + public AuidsType getAuids() + { + return auids; + } + + /** + * Sets the value of the auids property. + * + * @param auids the auids to set. + */ + public void setAuids(AuidsType auids) + { + this.auids = auids; + } + + /** + * Gets the value of the extensions property. + * + * @return the extensions property. + */ + public ExtensionsType getExtensions() + { + return extensions; + } + + /** + * Sets the value of the extensions property. + * + * @param extensions the extensions to set. + */ + public void setExtensions(ExtensionsType extensions) + { + this.extensions = extensions; + } + + /** + * Gets the value of the namespaces property. + * + * @return the namespaces property. + */ + public NamespacesType getNamespaces() + { + return namespaces; + } + + /** + * Sets the value of the namespaces property. + * + * @param namespaces the namespaces to set. + */ + public void setNamespaces(NamespacesType namespaces) + { + this.namespaces = namespaces; + } + + /** + * Gets the value of the any property. + * + * @return the any property. + */ + public List<Element> getAny() + { + if (any == null) + { + any = new ArrayList<Element>(); + } + return this.any; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/xcap-caps.xsd b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/xcap-caps.xsd new file mode 100644 index 0000000..23515f7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcapcaps/xcap-caps.xsd @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema targetNamespace="urn:ietf:params:xml:ns:xcap-caps" + xmlns="urn:ietf:params:xml:ns:xcap-caps" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" + attributeFormDefault="unqualified"> + <xs:element name="xcap-caps"> + <xs:annotation> + <xs:documentation>Root element for xcap-caps</xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element name="auids"> + <xs:annotation> + <xs:documentation>List of supported AUID. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:element name="auid" type="auidType"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="extensions" minOccurs="0"> + <xs:annotation> + <xs:documentation>List of supported extensions. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:element name="extension" type="extensionType"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="namespaces"> + <xs:annotation> + <xs:documentation>List of supported namespaces. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="unbounded"> + <xs:element name="namespace" type="namespaceType"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:any namespace="##other" processContents="lax" + minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:simpleType name="auidType"> + <xs:annotation> + <xs:documentation>AUID Type</xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"/> + </xs:simpleType> + <xs:simpleType name="extensionType"> + <xs:annotation> + <xs:documentation>Extension Type</xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"/> + </xs:simpleType> + <xs:simpleType name="namespaceType"> + <xs:annotation> + <xs:documentation>Namespace type</xs:documentation> + </xs:annotation> + <xs:restriction base="xs:anyURI"/> + </xs:simpleType> +</xs:schema> diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/BaseXCapError.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/BaseXCapError.java new file mode 100644 index 0000000..06f38ea --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/BaseXCapError.java @@ -0,0 +1,50 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The base XCAP error. + * + * @author Grigorii Balutsel + */ +public abstract class BaseXCapError implements XCapError +{ + /** + * The phrase attribute. + */ + private String phrase; + + /** + * Creates the XCAP error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public BaseXCapError(String phrase) + { + this.phrase = phrase; + } + + /** + * Gets the phrase attribute. + * + * @return User readable error description. + */ + public String getPhrase() + { + return phrase; + } + + /** + * Sets the value of the phrase property. + * + * @param phrase the phrase to set. + */ + void setPhrase(String phrase) + { + this.phrase = phrase; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/CannotDeleteType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/CannotDeleteType.java new file mode 100644 index 0000000..a6817fa --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/CannotDeleteType.java @@ -0,0 +1,28 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP cannot-delete element. Indicates that the requested DELETE operation + * could not be performed because it would not be idempotent. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class CannotDeleteType extends BaseXCapError +{ + /** + * Creates the XCAP cannot-delete error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public CannotDeleteType(String phrase) + { + super(phrase); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/CannotInsertType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/CannotInsertType.java new file mode 100644 index 0000000..7b85109 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/CannotInsertType.java @@ -0,0 +1,29 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP cannot-insert element. Indicates that the requested PUT operation + * could not be performed because a GET of that resource after the PUT would not + * yield the content of the PUT request. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class CannotInsertType extends BaseXCapError +{ + /** + * Creates the XCAP cannot-insert error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public CannotInsertType(String phrase) + { + super(phrase); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/ConstraintFailureType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/ConstraintFailureType.java new file mode 100644 index 0000000..8d62072 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/ConstraintFailureType.java @@ -0,0 +1,29 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP constraint-failure element. Indicates that the requested operation + * would result in a document that failed a data constraint defined by the + * application usage, but not enforced by the schema or a uniqueness constraint. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class ConstraintFailureType extends BaseXCapError +{ + /** + * Creates the XCAP constraint-failure with phrase attribute. + * + * @param phrase the phrase to set. + */ + public ConstraintFailureType(String phrase) + { + super(phrase); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/ExtensionType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/ExtensionType.java new file mode 100644 index 0000000..ed17459 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/ExtensionType.java @@ -0,0 +1,51 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +import java.util.*; + +import org.w3c.dom.*; + +/** + * The XCAP extension element. Indicates an error condition that is defined by + * an extension to XCAP. Clients that do not understand the content of the + * extension element MUST discard the xcap-error document and treat the error + * as an unqualified 409. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class ExtensionType extends BaseXCapError +{ + /** + * The list of any elements. + */ + private List<Element> any; + + /** + * Creates the XCAP extension error. + */ + public ExtensionType() + { + super(null); + } + + /** + * Gets the value of the any property. + * + * @return the any property. + */ + public List<Element> getAny() + { + if (any == null) + { + any = new ArrayList<Element>(); + } + return this.any; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NoParentType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NoParentType.java new file mode 100644 index 0000000..8541ff1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NoParentType.java @@ -0,0 +1,64 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP no-parent element. Indicates that an attempt to insert a document, + * element, or attribute failed because the directory, document, or element into + * which the insertion was supposed to occur does not exist. This error element + * can contain an optional ancestor element, which provides an HTTP URI that + * represents the closest parent that would be a valid point of insertion. This + * HTTP URI MAY be a relative URI, relative to the document itself. Because this + * is a valid HTTP URI, its node selector component MUST be percent-encoded. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class NoParentType extends BaseXCapError +{ + /** + * The ancestor element. HTTP uri that represents the closest parent that + * would be a valid point of insertion. + */ + protected String ancestor; + + NoParentType() + { + super(null); + } + + /** + * Creates the XCAP no-parent error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public NoParentType(String phrase) + { + super(phrase); + } + + /** + * Gets the phrase attribute. + * + * @return User readable error description. + */ + public String getAncestor() + { + return ancestor; + } + + /** + * Sets the value of the ancestor property. + * + * @param ancestor the ancestor to set. + */ + void setAncestor(String ancestor) + { + this.ancestor = ancestor; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotUtf8Type.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotUtf8Type.java new file mode 100644 index 0000000..9076944 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotUtf8Type.java @@ -0,0 +1,28 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP not-utf-8 element. Indicates that the request could not be completed + * because it would have produced a document not encoded in UTF-8. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class NotUtf8Type extends BaseXCapError +{ + /** + * Creates the XCAP not-utf-8 error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public NotUtf8Type(String phrase) + { + super(phrase); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotWellFormedType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotWellFormedType.java new file mode 100644 index 0000000..22fc72a --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotWellFormedType.java @@ -0,0 +1,28 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP not-well-formed element. Indicates that the body of the request was + * not a well-formed XML document. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class NotWellFormedType extends BaseXCapError +{ + /** + * Creates the XCAP not-well-formed error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public NotWellFormedType(String phrase) + { + super(phrase); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotXmlAttValueType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotXmlAttValueType.java new file mode 100644 index 0000000..44c7f12 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotXmlAttValueType.java @@ -0,0 +1,28 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP not-xml-att-value element. Indicates that the request was supposed + * to contain a valid XML attribute value, but did not. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class NotXmlAttValueType extends BaseXCapError +{ + /** + * Creates the XCAP not-xml-att-value error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public NotXmlAttValueType(String phrase) + { + super(phrase); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotXmlFragType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotXmlFragType.java new file mode 100644 index 0000000..46897dd --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/NotXmlFragType.java @@ -0,0 +1,29 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP not-xml-frag element. Indicates that the request was supposed to + * contain a valid XML fragment body, but did not. Most likely this is because + * the XML in the body was malformed or not balanced. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class NotXmlFragType extends BaseXCapError +{ + /** + * Creates the XCAP not-xml-frag error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public NotXmlFragType(String phrase) + { + super(phrase); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/SchemaValidationErrorType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/SchemaValidationErrorType.java new file mode 100644 index 0000000..43c5533 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/SchemaValidationErrorType.java @@ -0,0 +1,28 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP schema-validation-error element. Indicates that the document was not + * compliant to the schema after the requested operation was performed. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class SchemaValidationErrorType extends BaseXCapError +{ + /** + * Creates the XCAP schema-validation-error error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public SchemaValidationErrorType(String phrase) + { + super(phrase); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/UniquenessFailureType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/UniquenessFailureType.java new file mode 100644 index 0000000..a78e462 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/UniquenessFailureType.java @@ -0,0 +1,118 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +import java.util.*; + +/** + * The XCAP uniqueness-failure element. Indicates that the requested operation + * would result in a document that did not meet a uniqueness constraint defined + * by the application usage. For each URI, element, or attribute specified by + * the client that is not unique, an exists element is present as the content of + * the error element. Each exists element has a "field" attribute that contains + * a relative URI identifying the XML element or attribute whose value needs to + * be unique, but wasn't. The relative URI is relative to the document itself, + * and will therefore start with the root element. The query component of the + * URI MUST be present if the node selector portion of the URI contains + * namespace prefixes. Since the "field" node selector is a valid HTTP URI, it + * MUST be percent-encoded. The exists element can optionally contain a list of + * alt-value elements. Each one is a suggested alternate value that does not + * currently exist on the server. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class UniquenessFailureType extends BaseXCapError +{ + /** + * The list of exists elements. + */ + protected List<ExistsType> exists; + + /** + * Creates the XCAP uniqueness-failure error. + */ + UniquenessFailureType() + { + super(null); + } + + /** + * Creates the XCAP uniqueness-failure error with phrase attribute. + * + * @param phrase the phrase to set. + */ + public UniquenessFailureType(String phrase) + { + super(phrase); + } + + /** + * Gets the exists attribute. + * + * @return the exists property. + */ + public List<ExistsType> getExists() + { + if (exists == null) + { + exists = new ArrayList<ExistsType>(); + } + return this.exists; + } + + /** + * The XCAP exists element. + */ + public static class ExistsType + { + /** + * The list of alt-value elements. + */ + protected List<String> altValue; + + /** + * The field attribute. + */ + protected String field; + + /** + * Gets the altValue attribute. + * + * @return the altValue property. + */ + public List<String> getAltValue() + { + if (altValue == null) + { + altValue = new ArrayList<String>(); + } + return this.altValue; + } + + /** + * Gets the field attribute. + * + * @return the field property. + */ + public String getField() + { + return field; + } + + /** + * Sets the value of the field property. + * + * @param field the field to set. + */ + public void setField(String field) + { + this.field = field; + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/XCapError.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/XCapError.java new file mode 100644 index 0000000..e49ab03 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/XCapError.java @@ -0,0 +1,22 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP error inteface. + * + * @author Grigorii Balutsel + */ +public interface XCapError +{ + /** + * Gets the phrase attribute. + * + * @return User readable error description. + */ + public String getPhrase(); +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/XCapErrorParser.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/XCapErrorParser.java new file mode 100644 index 0000000..f757916 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/XCapErrorParser.java @@ -0,0 +1,514 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +import net.java.sip.communicator.impl.protocol.sip.xcap.model.*; +import static net.java.sip.communicator.impl.protocol.sip.xcap.model.StringUtils.*; +import static net.java.sip.communicator.impl.protocol.sip.xcap.model.XmlUtils.*; +import org.w3c.dom.*; + +/** + * Utility class that helps to converts xcap-error xml to the object model and + * object model to the xcap-error xml. + * + * @author Grigorii Balutsel + */ + +public final class XCapErrorParser +{ + private static final String NAMESPACE = "urn:ietf:params:xml:ns:xcap-error"; + + private static final String XCAP_ERROR_ELEMENT = "xcap-error"; + + private static final String CANNOT_DELETE_ELEMENT = "cannot-delete"; + + private static final String CANNOT_INSERT_ELEMENT = "cannot-insert"; + + private static final String CONSTRAINT_FAILURE_ELEMENT = + "constraint-failure"; + + private static final String EXTENSION_ELEMENT = "extension"; + + private static final String NOPARENT_ELEMENT = "no-parent"; + + private static final String NOPARENT_ANCESTOR_ELEMENT = "ancestor"; + + private static final String NOT_UTF8_ELEMENT = "not-utf-8"; + + private static final String NOT_WELL_FORMED_ELEMENT = "not-well-formed"; + + private static final String NOT_XML_ATT_VALUE_ELEMENT = "not-xml-att-value"; + + private static final String NOT_XMLF_FRAG_ELEMENT = "not-xml-frag"; + + private static final String SCHEMA_VALIDATION_ERROR_ELEMENT = + "schema-validation-error"; + + private static final String UNIQUENESS_FAILURE_ELEMENT = + "uniqueness-failure"; + + private static final String UNIQUENESS_FAILURE_EXISTS_ELEMENT = + "exists"; + + private static final String UNIQUENESS_FAILURE_EXISTS_FIELD_ATTR = + "field"; + + private static final String UNIQUENESS_FAILURE_EXISTS_ALT_VALUE_ELEMENT = + "alt-value"; + + private static final String PHRASE_ATTR = "phrase"; + + /** + * Creates xcap-error object from the element. + * + * @param xml the XML to analyze. + * @return the resource-lists object. + * @throws ParsingException if there is some error during parsing. + */ + public static XCapErrorType fromXml(String xml) + throws ParsingException + { + if (isNullOrEmpty(xml)) + { + throw new IllegalArgumentException("XML cannot be null or empty"); + } + try + { + XCapErrorType error = new XCapErrorType(); + Document document = createDocument(xml); + Element xCapErrorElement = document.getDocumentElement(); + if (XCAP_ERROR_ELEMENT.equals(xCapErrorElement.getLocalName()) && + !NAMESPACE.equals(xCapErrorElement.getNamespaceURI())) + { + throw new Exception("Document doesn't contain xcap-error " + + "element"); + } + // Process attributes + NamedNodeMap attributes = xCapErrorElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("xcap-error element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + throw new Exception("xcap-error element is invalid"); + } + // Process elements + NodeList childNodes = xCapErrorElement.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element element = (Element) node; + String namespaceUri = getNamespaceUri(element); + if (!NAMESPACE.equals(namespaceUri)) + { + throw new Exception("xcap-error element is invalid"); + } + error.setError(errorFromElement(element)); + } + return error; + } + catch (Exception ex) + { + throw new ParsingException(ex); + } + } + + /** + * Creates xcap-error object from the element. + * + * @param element the element to analyze. + * @return the list object. + * @throws Exception if there is some error during parsing. + */ + private static XCapError errorFromElement(Element element) + throws Exception + { + XCapError error; + if (!NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("error-element element is invalid"); + } + String localName = element.getLocalName(); + if (CANNOT_DELETE_ELEMENT.equals(localName)) + { + error = new CannotDeleteType(getPhraseAttribute(element)); + } + else if (CANNOT_INSERT_ELEMENT.equals(localName)) + { + error = new CannotInsertType(getPhraseAttribute(element)); + } + else if (CONSTRAINT_FAILURE_ELEMENT.equals(localName)) + { + error = new ConstraintFailureType(getPhraseAttribute(element)); + } + else if (EXTENSION_ELEMENT.equals(localName)) + { + error = getExtensionFromElement(element); + } + else if (NOPARENT_ELEMENT.equals(localName)) + { + error = getNoParentFromElement(element); + } + else if (NOT_UTF8_ELEMENT.equals(localName)) + { + error = new NotUtf8Type(getPhraseAttribute(element)); + } + else if (NOT_WELL_FORMED_ELEMENT.equals(localName)) + { + error = new NotWellFormedType(getPhraseAttribute(element)); + } + else if (NOT_XML_ATT_VALUE_ELEMENT.equals(localName)) + { + error = new NotXmlAttValueType(getPhraseAttribute(element)); + } + else if (NOT_XMLF_FRAG_ELEMENT.equals(localName)) + { + error = new NotXmlAttValueType(getPhraseAttribute(element)); + } + else if (SCHEMA_VALIDATION_ERROR_ELEMENT.equals(localName)) + { + error = new SchemaValidationErrorType( + getPhraseAttribute(element)); + } + else if (UNIQUENESS_FAILURE_ELEMENT.equals(element.getLocalName())) + { + error = getUniquenessFailureFromElement(element); + } + else + { + throw new Exception("content element is invalid"); + } + return error; + } + + /** + * Gets phrase value from the element. + * + * @param element the element to analyze. + * @return the list object. + * @throws Exception if there is some error during parsing. + */ + private static String getPhraseAttribute(Element element) + throws Exception + { + String result = null; + if (!NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("error element is invalid"); + } + // Process attributes + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("data element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (!NAMESPACE.equals(namespaceUri) || + !PHRASE_ATTR.equals(attribute.getLocalName()) || + result != null) + { + throw new Exception("error element is invalid"); + } + result = attribute.getValue(); + } + // Process elements + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) + { + throw new Exception("error element is invalid"); + } + } + return result; + } + + /** + * Creates extension object from the element. + * + * @param element the element to analyze. + * @return the list object. + * @throws Exception if there is some error during parsing. + */ + private static ExtensionType getExtensionFromElement(Element element) + throws Exception + { + ExtensionType result = new ExtensionType(); + if (!EXTENSION_ELEMENT.equals(element.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("extension element is invalid"); + } + // Process attributes + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("extension element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + throw new Exception("extension element is invalid"); + } + // Process elements + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element childElement = (Element) node; + String namespaceUri = getNamespaceUri(childElement); + if (namespaceUri == null) + { + throw new Exception("extension element is invalid"); + } + if (NAMESPACE.equals(namespaceUri)) + { + throw new Exception("extension element is invalid"); + } + result.getAny().add(childElement); + } + return result; + } + + /** + * Creates no-parent object from the element. + * + * @param element the element to analyze. + * @return the list object. + * @throws Exception if there is some error during parsing. + */ + private static NoParentType getNoParentFromElement(Element element) + throws Exception + { + NoParentType result = new NoParentType(); + if (!NOPARENT_ELEMENT.equals(element.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("no-parent element is invalid"); + } + String phrase = null; + String ancestor = null; + // Process attributes + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("no-parent element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (!NAMESPACE.equals(namespaceUri) || + !PHRASE_ATTR.equals(attribute.getLocalName()) || + phrase != null) + { + throw new Exception("no-parent element is invalid"); + } + phrase = attribute.getValue(); + } + // Process elements + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element childElement = (Element) node; + String namespaceUri = getNamespaceUri(childElement); + String localName = childElement.getLocalName(); + if (namespaceUri == null) + { + throw new Exception("no-parent element is invalid"); + } + if (!NAMESPACE.equals(namespaceUri) || + !NOPARENT_ANCESTOR_ELEMENT.equals(localName) || + ancestor != null) + { + throw new Exception("no-parent element is invalid"); + } + ancestor = childElement.getTextContent(); + } + result.setPhrase(phrase); + result.setAncestor(ancestor); + return result; + } + + /** + * Creates uniqueness-failure object from the element. + * + * @param element the element to analyze. + * @return the list object. + * @throws Exception if there is some error during parsing. + */ + private static UniquenessFailureType getUniquenessFailureFromElement( + Element element) + throws Exception + { + UniquenessFailureType result = new UniquenessFailureType(); + if (!UNIQUENESS_FAILURE_ELEMENT.equals(element.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("uniqueness-failure element is invalid"); + } + String phrase = null; + // Process attributes + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("uniqueness-failure element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (!NAMESPACE.equals(namespaceUri) || + !PHRASE_ATTR.equals(attribute.getLocalName()) || + phrase != null) + { + throw new Exception("uniqueness-failure element is invalid"); + } + phrase = attribute.getValue(); + } + // Process elements + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element childElement = (Element) node; + String namespaceUri = getNamespaceUri(childElement); + String localName = childElement.getLocalName(); + if (namespaceUri == null) + { + throw new Exception("uniqueness-failure element is invalid"); + } + if (!NAMESPACE.equals(namespaceUri) || + !UNIQUENESS_FAILURE_EXISTS_ELEMENT.equals(localName)) + { + throw new Exception("uniqueness-failure element is invalid"); + } + result.getExists().add(getExistsFromElement(childElement)); + } + result.setPhrase(phrase); + return result; + } + + /** + * Creates exists object from the element. + * + * @param element the element to analyze. + * @return the list object. + * @throws Exception if there is some error during parsing. + */ + private static UniquenessFailureType.ExistsType getExistsFromElement( + Element element) + throws Exception + { + UniquenessFailureType.ExistsType result = + new UniquenessFailureType.ExistsType(); + if (!UNIQUENESS_FAILURE_EXISTS_ELEMENT.equals(element.getLocalName()) || + !NAMESPACE.equals(getNamespaceUri(element))) + { + throw new Exception("exists element is invalid"); + } + String field = null; + // Process attributes + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) + { + Attr attribute = (Attr) attributes.item(i); + String namespaceUri = getNamespaceUri(attribute); + if (namespaceUri == null) + { + throw new Exception("exists element is invalid"); + } + if (isStandartXmlNamespace(namespaceUri)) + { + continue; + } + if (!NAMESPACE.equals(namespaceUri) || + !UNIQUENESS_FAILURE_EXISTS_FIELD_ATTR + .equals(attribute.getLocalName()) || + field != null) + { + throw new Exception("exists element is invalid"); + } + field = attribute.getValue(); + } + // Process elements + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) + { + Node node = childNodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) + { + continue; + } + Element childElement = (Element) node; + String namespaceUri = getNamespaceUri(childElement); + String localName = childElement.getLocalName(); + if (namespaceUri == null) + { + throw new Exception("exists element is invalid"); + } + if (!NAMESPACE.equals(namespaceUri) || + !UNIQUENESS_FAILURE_EXISTS_ALT_VALUE_ELEMENT + .equals(localName)) + { + throw new Exception("exists element is invalid"); + } + result.getAltValue().add(childElement.getTextContent()); + } + if (field == null) + { + throw new Exception("exists element is invalid"); + } + result.setField(field); + return result; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/XCapErrorType.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/XCapErrorType.java new file mode 100644 index 0000000..a6524ba --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/XCapErrorType.java @@ -0,0 +1,42 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror; + +/** + * The XCAP xcap-error element. Indicates the reason for the error. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class XCapErrorType +{ + /** + * The error element. + */ + private XCapError error; + + /** + * Gets the value of the error property. + * + * @return the error property. + */ + public XCapError getError() + { + return error; + } + + /** + * Sets the value of the error property. + * + * @param error the uri to set. + */ + public void setError(XCapError error) + { + this.error = error; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/xcap-error.xsd b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/xcap-error.xsd new file mode 100644 index 0000000..9efbfe5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/model/xcaperror/xcap-error.xsd @@ -0,0 +1,187 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema targetNamespace="urn:ietf:params:xml:ns:xcap-error" + xmlns="urn:ietf:params:xml:ns:xcap-error" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" + attributeFormDefault="unqualified"> + + <xs:element name="error-element" abstract="true"/> + <xs:element name="xcap-error"> + <xs:annotation> + <xs:documentation>Indicates the reason for the error. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element ref="error-element"/> + </xs:sequence> + </xs:complexType> + </xs:element> + + <xs:element name="extension" substitutionGroup="error-element"> + <xs:complexType> + <xs:sequence> + <xs:any namespace="##any" processContents="lax" + minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + + <xs:element name="schema-validation-error" + substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This element indicates that the document was not + compliant to the schema after the requested operation was + performed. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> + + <xs:element name="not-xml-frag" substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This indicates that the request was supposed to + contain a valid XML fragment body, but did not. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> + + <xs:element name="no-parent" substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This indicates that an attempt to insert an + element, attribute, or document failed because the document or + element into which the insertion was supposed to occur does not + exist. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element name="ancestor" type="xs:anyURI" minOccurs="0"> + <xs:annotation> + <xs:documentation>Contains an HTTP URI that points to + the element that is the closest ancestor that does + exist. + </xs:documentation> + </xs:annotation> + </xs:element> + </xs:sequence> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> + + <xs:element name="cannot-insert" substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This indicates that the requested PUT operation + could not be performed because a GET of that resource after the + PUT would not yield the content of the PUT request. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> + + <xs:element name="not-xml-att-value" + substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This indicates that the request was supposed to + contain a valid XML attribute value, but did not. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> + + <xs:element name="uniqueness-failure" + substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This indicates that the requested operation would + result in a document that did not meet a uniqueness constraint + defined by the application usage. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element name="exists" maxOccurs="unbounded"> + <xs:annotation> + <xs:documentation>For each URI, element, or attribute + specified by the client that is not unique, one of + these is present. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence minOccurs="0"> + <xs:element name="alt-value" type="xs:string" + maxOccurs="unbounded"> + <xs:annotation> + <xs:documentation>An optional set of + alternate values can be provided. + </xs:documentation> + </xs:annotation> + </xs:element> + </xs:sequence> + <xs:attribute name="field" type="xs:string" + use="required"/> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> + + <xs:element name="not-well-formed" + substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This indicates that the body of the request was + not a well-formed document. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> + + <xs:element name="constraint-failure" + substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This indicates that the requested operation would + result in a document that failed a data constraint defined by + the application usage, but not enforced by the schema or a + uniqueness constraint. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> + + <xs:element name="cannot-delete" substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This indicates that the requested DELETE + operation could not be performed because it would not be + idempotent. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> + + <xs:element name="not-utf-8" substitutionGroup="error-element"> + <xs:annotation> + <xs:documentation>This indicates that the request could not be + completed because it would have produced a document not encoded + in UTF-8. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="phrase" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/src/net/java/sip/communicator/impl/protocol/sip/xcap/utils/StreamUtils.java b/src/net/java/sip/communicator/impl/protocol/sip/xcap/utils/StreamUtils.java new file mode 100644 index 0000000..de28cc8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/xcap/utils/StreamUtils.java @@ -0,0 +1,66 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.sip.xcap.utils; + +import java.io.*; + +/** + * Base HTTP XCAP client implementation. + * <p/> + * Compliant with rfc4825 + * + * @author Grigorii Balutsel + */ +public class StreamUtils +{ + /** + * The buffer size (1kb). + */ + private static int BUFFER_SIZE = 1024; + + /** + * This class cannot be instanced. + */ + private StreamUtils() + { + } + + /** + * Reads content from the stream. + * + * @param source the input stream. + * @return the content. + * @throws IOException if there is some error during read + * operation. + * @throws IllegalArgumentException if source is null. + */ + public static byte[] read(InputStream source) + throws IOException + { + if (source == null) + { + throw new IllegalArgumentException("Input parameter can't be null"); + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try + { + byte[] buffer = new byte[BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = source.read(buffer)) > -1) + { + out.write(buffer, 0, bytesRead); + } + return out.toByteArray(); + } + finally + { + source.close(); + out.close(); + } + } +} + diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/PresencePanel.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/PresencePanel.java index a9eea7a..f0c1ccf 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/PresencePanel.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/PresencePanel.java @@ -16,42 +16,79 @@ import net.java.sip.communicator.util.swing.*; * The <tt>PresencePanel</tt> is the one containing presence information. * * @author Yana Stamcheva + * @author Grigorii Balutsel */ public class PresencePanel - extends TransparentPanel + extends TransparentPanel { private JPanel presenceOpPanel - = new TransparentPanel(new BorderLayout(10, 10)); + = new TransparentPanel(new BorderLayout(10, 10)); private JPanel buttonsPresOpPanel = - new TransparentPanel(new GridLayout(0, 1, 10, 10)); + new TransparentPanel(new GridLayout(0, 1, 10, 10)); private JPanel labelsPresOpPanel - = new TransparentPanel(new GridLayout(0, 1, 10, 10)); + = new TransparentPanel(new GridLayout(0, 1, 10, 10)); private JPanel valuesPresOpPanel - = new TransparentPanel(new GridLayout(0, 1, 10, 10)); + = new TransparentPanel(new GridLayout(0, 1, 10, 10)); private JCheckBox enablePresOpButton = - new SIPCommCheckBox(Resources - .getString("plugin.sipaccregwizz.ENABLE_PRESENCE"), true); + new SIPCommCheckBox(Resources + .getString("plugin.sipaccregwizz.ENABLE_PRESENCE"), true); private JCheckBox forceP2PPresOpButton = - new SIPCommCheckBox(Resources - .getString("plugin.sipaccregwizz.FORCE_P2P_PRESENCE"), false); + new SIPCommCheckBox(Resources + .getString("plugin.sipaccregwizz.FORCE_P2P_PRESENCE"), + false); private JLabel pollPeriodLabel = new JLabel( - Resources.getString( - "plugin.sipaccregwizz.OFFLINE_CONTACT_POLLING_PERIOD")); + Resources.getString( + "plugin.sipaccregwizz.OFFLINE_CONTACT_POLLING_PERIOD")); private JLabel subscribeExpiresLabel = new JLabel( - Resources.getString("plugin.sipaccregwizz.SUBSCRIPTION_EXPIRATION")); + Resources.getString("plugin.sipaccregwizz.SUBSCRIPTION_EXPIRATION")); private JTextField pollPeriodField - = new JTextField(SIPAccountRegistration.DEFAULT_POLL_PERIOD); + = new JTextField(SIPAccountRegistration.DEFAULT_POLL_PERIOD); private JTextField subscribeExpiresField = - new JTextField(SIPAccountRegistration.DEFAULT_SUBSCRIBE_EXPIRES); + new JTextField(SIPAccountRegistration.DEFAULT_SUBSCRIBE_EXPIRES); + + private JPanel xCapPanel + = new TransparentPanel(new BorderLayout(10, 10)); + + private JPanel xCapButtonsPanel + = new TransparentPanel(new GridLayout(0, 1, 10, 10)); + + private JPanel xCapLabelsPanel + = new TransparentPanel(new GridLayout(0, 1, 10, 10)); + + private JPanel xCapValuesPanel + = new TransparentPanel(new GridLayout(0, 1, 10, 10)); + + private JLabel xCapServerUriLabel = new JLabel( + Resources.getString("plugin.sipaccregwizz.XCAP_SERVER_URI")); + + private JLabel xCapUserLabel = new JLabel( + Resources.getString("plugin.sipaccregwizz.XCAP_USER")); + + private JLabel xCapPasswordLabel = new JLabel( + Resources.getString("plugin.sipaccregwizz.XCAP_PASSWORD")); + + private JTextField xCapServerUriValue = new JTextField(); + + private JTextField xCapUserValue = new JTextField(); + + private JPasswordField xCapPasswordValue = new JPasswordField(); + + private JCheckBox xCapEnableBox = new SIPCommCheckBox( + Resources.getString("plugin.sipaccregwizz.XCAP_ENABLE"), + false); + + private JCheckBox xCapUseSipCredetialsBox = new SIPCommCheckBox( + Resources.getString("plugin.sipaccregwizz.XCAP_USE_SIP_CREDETIALS"), + true); /** * Creates an instance of <tt>PresencePanel</tt>. @@ -85,16 +122,54 @@ public class PresencePanel presenceOpPanel.add(valuesPresOpPanel, BorderLayout.CENTER); presenceOpPanel.setBorder(BorderFactory.createTitledBorder( - Resources.getString("plugin.sipaccregwizz.PRESENCE_OPTIONS"))); + Resources.getString("plugin.sipaccregwizz.PRESENCE_OPTIONS"))); + + xCapEnableBox.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent evt) + { + JCheckBox checkBox = (JCheckBox) evt.getSource(); + setXCapEnableEnabled(checkBox.isSelected()); + } + }); + xCapButtonsPanel.add(xCapEnableBox); + + xCapUseSipCredetialsBox.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent evt) + { + JCheckBox checkBox = (JCheckBox) evt.getSource(); + setXCapUseSipCredetialsEnabled(checkBox.isSelected()); + } + }); + xCapButtonsPanel.add(xCapUseSipCredetialsBox); + + setXCapEnableEnabled(xCapEnableBox.isSelected()); + + xCapLabelsPanel.add(xCapServerUriLabel); + xCapLabelsPanel.add(xCapUserLabel); + xCapLabelsPanel.add(xCapPasswordLabel); + + xCapValuesPanel.add(xCapServerUriValue); + xCapValuesPanel.add(xCapUserValue); + xCapValuesPanel.add(xCapPasswordValue); + + xCapPanel.add(xCapButtonsPanel, BorderLayout.NORTH); + xCapPanel.add(xCapLabelsPanel, BorderLayout.WEST); + xCapPanel.add(xCapValuesPanel, BorderLayout.CENTER); + + xCapPanel.setBorder(BorderFactory.createTitledBorder( + Resources.getString("plugin.sipaccregwizz.XCAP_OPTIONS"))); this.add(presenceOpPanel, BorderLayout.NORTH); + this.add(xCapPanel, BorderLayout.SOUTH); } /** * Enables or disable all presence related options. * * @param isEnabled <code>true</code> to enable the presence related - * options, <code>false</code> - to disable them. + * options, <code>false</code> - to disable them. */ void setPresenceOptionsEnabled(boolean isEnabled) { @@ -104,9 +179,42 @@ public class PresencePanel } /** + * Enables or disable XCAP credetials related options. + * + * @param isEnabled <code>true</code> to enable the credetials related + * options, <code>false</code> - to disable them. + */ + void setXCapUseSipCredetialsEnabled(boolean isEnabled) + { + xCapUserValue.setEnabled(!isEnabled); + xCapPasswordValue.setEnabled(!isEnabled); + } + + /** + * Enables or disable XCAP related options. + * + * @param isEnabled <code>true</code> to enable the XCAP related + * options, <code>false</code> - to disable them. + */ + void setXCapEnableEnabled(boolean isEnabled) + { + xCapUseSipCredetialsBox.setEnabled(isEnabled); + xCapServerUriValue.setEnabled(isEnabled); + if(isEnabled) + { + setXCapUseSipCredetialsEnabled(xCapUseSipCredetialsBox.isSelected()); + } + else + { + setXCapUseSipCredetialsEnabled(true); + } + } + + /** * Indicates if the presence is enabled. + * * @return <tt>true</tt> if the presence is enabled, <tt>false</tt> - - * otherwise + * otherwise */ boolean isPresenceEnabled() { @@ -115,8 +223,9 @@ public class PresencePanel /** * Enables/disables the presence. + * * @param isPresenceEnabled <tt>true</tt> to enable the presence, - * <tt>false</tt> - otherwise + * <tt>false</tt> - otherwise */ void setPresenceEnabled(boolean isPresenceEnabled) { @@ -125,8 +234,9 @@ public class PresencePanel /** * Indicates if the peer-to-peer presence mode is selected. + * * @return <tt>true</tt> if the peer-to-peer presence mode is selected, - * <tt>false</tt> - otherwise. + * <tt>false</tt> - otherwise. */ boolean isForcePeerToPeerMode() { @@ -135,8 +245,9 @@ public class PresencePanel /** * Enables/disables the peer-to-peer presence mode. + * * @param forceP2P <tt>true</tt> to select the peer-to-peer presence mode, - * <tt>false</tt> - otherwise. + * <tt>false</tt> - otherwise. */ void setForcePeerToPeerMode(boolean forceP2P) { @@ -145,6 +256,7 @@ public class PresencePanel /** * Returns the poll period. + * * @return the poll period */ String getPollPeriod() @@ -154,6 +266,7 @@ public class PresencePanel /** * Sets the poll period. + * * @param pollPeriod the poll period */ void setPollPeriod(String pollPeriod) @@ -163,6 +276,7 @@ public class PresencePanel /** * Returns the subscription expiration information. + * * @return the subscription expiration information */ String getSubscriptionExpiration() @@ -172,10 +286,113 @@ public class PresencePanel /** * Sets the subscription expiration information. + * * @param subscExp the subscription expiration information */ void setSubscriptionExpiration(String subscExp) { subscribeExpiresField.setText(subscExp); } + + /** + * Indicates if XCAP has to use its capabilities. + * + * @return <tt>true</tt> if XCAP has to use its capabilities, + * <tt>false</tt> - otherwise. + */ + boolean isXCapEnable() + { + return xCapEnableBox.isSelected(); + } + + /** + * Sets if has to use its capabilities. + * + * @param xCapEnable if has to use its capabilities. + */ + void setXCapEnable(boolean xCapEnable) + { + xCapEnableBox.setSelected(xCapEnable); + } + + /** + * Indicates if XCAP has to use SIP account credetials. + * + * @return <tt>true</tt> if XCAP has to use SIP account credetials, + * <tt>false</tt> - otherwise. + */ + boolean isXCapUseSipCredetials() + { + return xCapUseSipCredetialsBox.isSelected(); + } + + /** + * Sets if XCAP has to use SIP account credetials. + * + * @param xCapUseSipCredetials if XCAP has to use SIP account credetials. + */ + void setXCapUseSipCredetials(boolean xCapUseSipCredetials) + { + xCapUseSipCredetialsBox.setSelected(xCapUseSipCredetials); + } + + /** + * Gets the XCAP server uri. + * + * @return the XCAP server uri. + */ + String getXCapServerUri() + { + return xCapServerUriValue.getText(); + } + + /** + * Sets the XCAP server uri. + * + * @param xCapServerUri the XCAP server uri. + */ + void setXCapServerUri(String xCapServerUri) + { + xCapServerUriValue.setText(xCapServerUri); + } + + /** + * Gets the XCAP user. + * + * @return the XCAP user. + */ + String getXCapUser() + { + return xCapUserValue.getText(); + } + + /** + * Sets the XCAP user. + * + * @param xCapUser the XCAP user. + */ + void setXCapUser(String xCapUser) + { + xCapUserValue.setText(xCapUser); + } + + /** + * Gets the XCAP password. + * + * @return the XCAP password. + */ + char[] getXCapPassword() + { + return xCapPasswordValue.getPassword(); + } + + /** + * Sets the XCAP password. + * + * @param xCapPassword the XCAP password. + */ + void setXCapPassword(String xCapPassword) + { + xCapPasswordValue.setText(xCapPassword); + } } diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java index acb2bb2..4f3966d 100755 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java @@ -10,6 +10,7 @@ package net.java.sip.communicator.plugin.sipaccregwizz; * through the <tt>SIPAccountRegistrationWizard</tt>. * * @author Yana Stamcheva + * @author Grigorii Balutsel */ public class SIPAccountRegistration { @@ -65,6 +66,16 @@ public class SIPAccountRegistration private String defaultDomain = null; + private boolean xCapEnable = false; + + private boolean xCapUseSipCredetials = true; + + private String xCapServerUri; + + private String xCapUser; + + private String xCapPassword; + public String getPreferredTransport() { return preferredTransport; @@ -457,4 +468,104 @@ public class SIPAccountRegistration { this.defaultTransport = defaultTransport; } + + /** + * Checks if XCAP is enabled. + * + * @return true if XCAP is enabled otherwise false. + */ + public boolean isXCapEnable() + { + return xCapEnable; + } + /** + * Sets if XCAP is enable. + * + * @param xCapEnable XCAP enable. + */ + public void setXCapEnable(boolean xCapEnable) + { + this.xCapEnable = xCapEnable; + } + + /** + * Checks if XCAP has to use SIP account credetials. + * + * @return true if XCAP has to use SIP account credetials otherwise false. + */ + public boolean isXCapUseSipCredetials() + { + return xCapUseSipCredetials; + } + + /** + * Sets if XCAP has to use SIP account credetials. + * + * @param xCapUseSipCredetials if XCAP has to use SIP account credetials. + */ + public void setXCapUseSipCredetials(boolean xCapUseSipCredetials) + { + this.xCapUseSipCredetials = xCapUseSipCredetials; + } + + /** + * Gets the XCAP server uri. + * + * @return the XCAP server uri. + */ + public String getXCapServerUri() + { + return xCapServerUri; + } + + /** + * Sets the XCAP server uri. + * + * @param xCapServerUri the XCAP server uri. + */ + public void setXCapServerUri(String xCapServerUri) + { + this.xCapServerUri = xCapServerUri; + } + + /** + * Gets the XCAP user. + * + * @return the XCAP user. + */ + public String getXCapUser() + { + return xCapUser; + } + + /** + * Sets the XCAP user. + * + * @param xCapUser the XCAP user. + */ + public void setXCapUser(String xCapUser) + { + this.xCapUser = xCapUser; + } + + /** + * Gets the XCAP password. + * + * @return the XCAP password. + */ + public String getXCapPassword() + { + return xCapPassword; + } + + /** + * Sets the XCAP password. + * + * @param xCapPassword the XCAP password. + */ + public void setXCapPassword(String xCapPassword) + { + this.xCapPassword = xCapPassword; + } + } diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java index 275a79e..b792278 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java @@ -11,6 +11,7 @@ import net.java.sip.communicator.util.swing.*; * The <tt>SIPAccountRegistrationForm</tt>. * * @author Yana Stamcheva + * @author Grogorii Balutsel */ public class SIPAccountRegistrationForm extends TransparentPanel @@ -237,6 +238,14 @@ public class SIPAccountRegistrationForm SIPAccRegWizzActivator.getUIService().getAccountRegWizardContainer() .setBackButtonEnabled(true); + registration.setXCapEnable(presencePanel.isXCapEnable()); + registration.setXCapUseSipCredetials( + presencePanel.isXCapUseSipCredetials()); + registration.setXCapServerUri(presencePanel.getXCapServerUri()); + registration.setXCapUser(presencePanel.getXCapUser()); + registration + .setXCapPassword(new String(presencePanel.getXCapPassword())); + return true; } @@ -294,6 +303,17 @@ public class SIPAccountRegistrationForm String keepAliveInterval = accountID.getAccountPropertyString("KEEP_ALIVE_INTERVAL"); + boolean xCapEnable = accountID + .getAccountPropertyBoolean("XCAP_ENABLE", false); + boolean xCapUseSipCredetials = accountID + .getAccountPropertyBoolean("XCAP_USE_SIP_CREDETIALS", true); + String xCapServerUri = + accountID.getAccountPropertyString("XCAP_SERVER_URI"); + String xCapUser = + accountID.getAccountPropertyString("XCAP_USER"); + String xCapPassword = + accountID.getAccountPropertyString("XCAP_PASSWORD"); + connectionPanel.setServerOverridden( accountID.getAccountPropertyBoolean( ProtocolProviderFactory.IS_SERVER_OVERRIDDEN, false)); @@ -344,6 +364,14 @@ public class SIPAccountRegistrationForm connectionPanel.setKeepAliveMethod(keepAliveMethod); connectionPanel.setKeepAliveInterval(keepAliveInterval); + + presencePanel.setXCapEnable(xCapEnable); + presencePanel.setXCapEnableEnabled(xCapEnable); + presencePanel.setXCapUseSipCredetials(xCapUseSipCredetials); + presencePanel.setXCapUseSipCredetialsEnabled(xCapUseSipCredetials); + presencePanel.setXCapServerUri(xCapServerUri); + presencePanel.setXCapUser(xCapUser); + presencePanel.setXCapPassword(xCapPassword); } /** diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java index 4b3786d..d04cda7 100644 --- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java +++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java @@ -21,6 +21,7 @@ import org.osgi.framework.*; * the user to create and configure a new SIP account. * * @author Yana Stamcheva + * @author Grigorii Balutsel */ public class SIPAccountRegistrationWizard implements AccountRegistrationWizard @@ -130,8 +131,8 @@ public class SIPAccountRegistrationWizard */ public Iterator<Map.Entry<String, String>> getSummary() { - Hashtable<String, String> summaryTable - = new Hashtable<String, String>(); + LinkedHashMap<String, String> summaryTable + = new LinkedHashMap<String, String>(); boolean rememberPswd = registration.isRememberPassword(); String rememberPswdString = Resources.getString( @@ -234,6 +235,36 @@ public class SIPAccountRegistrationWizard Resources.getString("plugin.sipaccregwizz.KEEP_ALIVE_INTERVAL"), registration.getKeepAliveInterval()); + if (registration.isXCapEnable()) + { + summaryTable.put(Resources.getString( + "plugin.sipaccregwizz.XCAP_ENABLE"), + Resources.getString("service.gui.YES")); + + summaryTable.put(Resources.getString( + "plugin.sipaccregwizz.XCAP_SERVER_URI_SUMMARY"), + registration.getXCapServerUri()); + + if (registration.isXCapUseSipCredetials()) + { + summaryTable.put(Resources.getString( + "plugin.sipaccregwizz.XCAP_USE_SIP_CREDETIALS_SUMMARY"), + Resources.getString("service.gui.YES")); + } + else + { + summaryTable.put(Resources.getString( + "plugin.sipaccregwizz.XCAP_USER_SUMMARY"), + registration.getXCapUser()); + } + } + else + { + summaryTable.put(Resources.getString( + "plugin.sipaccregwizz.XCAP_ENABLE"), + Resources.getString("service.gui.NO")); + } + return summaryTable.entrySet().iterator(); } @@ -405,6 +436,30 @@ public class SIPAccountRegistrationWizard accountProperties.put("KEEP_ALIVE_INTERVAL", registration.getKeepAliveInterval()); + accountProperties.put("XCAP_ENABLE", + Boolean.toString(registration.isXCapEnable())); + + if(registration.isXCapEnable()) + { + accountProperties.put("XCAP_USE_SIP_CREDETIALS", + Boolean.toString(registration.isXCapUseSipCredetials())); + if (registration.getXCapServerUri() != null) + { + accountProperties + .put("XCAP_SERVER_URI", registration.getXCapServerUri()); + } + if (registration.getXCapUser() != null) + { + accountProperties + .put("XCAP_USER", registration.getXCapUser()); + } + if (registration.getXCapPassword() != null) + { + accountProperties + .put("XCAP_PASSWORD", registration.getXCapPassword()); + } + } + if(isModification) { accountProperties.put(ProtocolProviderFactory.USER_ID, userName); diff --git a/src/net/java/sip/communicator/service/protocol/ServerStoredDetails.java b/src/net/java/sip/communicator/service/protocol/ServerStoredDetails.java index b2def52..6de70fa 100644 --- a/src/net/java/sip/communicator/service/protocol/ServerStoredDetails.java +++ b/src/net/java/sip/communicator/service/protocol/ServerStoredDetails.java @@ -118,8 +118,12 @@ public class ServerStoredDetails if(!(obj instanceof GenericDetail)) return false; - GenericDetail other = (GenericDetail)obj; + if(this == obj) + { + return true; + } + GenericDetail other = (GenericDetail)obj; if(this.detailDisplayName != null // equals DisplayName && other.getDetailDisplayName() != null diff --git a/test/net/java/sip/communicator/slick/protocol/sip/SipProtocolProviderServiceLick.java b/test/net/java/sip/communicator/slick/protocol/sip/SipProtocolProviderServiceLick.java index 0e77da6..12993c4 100644 --- a/test/net/java/sip/communicator/slick/protocol/sip/SipProtocolProviderServiceLick.java +++ b/test/net/java/sip/communicator/slick/protocol/sip/SipProtocolProviderServiceLick.java @@ -41,7 +41,7 @@ public class SipProtocolProviderServiceLick */ public static final String DISABLE_ONLINE_TESTS_PROPERTY_NAME = "accounts.sip.DISABLE_ONLINE_TESTING"; - + /** * The name of the property the value of which is a formatted string that * contains the contact list that. @@ -50,6 +50,11 @@ public class SipProtocolProviderServiceLick = "accounts.sip.CONTACT_LIST"; /** + * The name of the property the value of which is XCAP server uri. + */ + public static final String XCAP_SERVER_PROPERTY_NAME = "XCAP_SERVER"; + + /** * Initializes and registers all tests that we'll run as a part of this * slick. * @@ -70,6 +75,9 @@ public class SipProtocolProviderServiceLick if (offlineMode != null && offlineMode.equalsIgnoreCase("true")) SipSlickFixture.onlineTestingDisabled = true; + + // xcap parsing tests + addTest(TestXCapParse.suite()); //First test account installation so that the service that has //been installed by it gets tested by the rest of the tests. @@ -93,6 +101,9 @@ public class SipProtocolProviderServiceLick // telephony addTestSuite(TestOperationSetBasicTelephonySipImpl.class); + + // Server stored info + addTest(TestOperationSetServerStoredInfo.suite()); } //This must remain after all other tests using the accounts diff --git a/test/net/java/sip/communicator/slick/protocol/sip/TestAccountInstallation.java b/test/net/java/sip/communicator/slick/protocol/sip/TestAccountInstallation.java index 7c9336e..a10aae1 100644 --- a/test/net/java/sip/communicator/slick/protocol/sip/TestAccountInstallation.java +++ b/test/net/java/sip/communicator/slick/protocol/sip/TestAccountInstallation.java @@ -6,11 +6,12 @@ */ package net.java.sip.communicator.slick.protocol.sip; -import java.util.*; - -import org.osgi.framework.*; import junit.framework.*; +import net.java.sip.communicator.impl.protocol.sip.*; import net.java.sip.communicator.service.protocol.*; +import org.osgi.framework.*; + +import java.util.*; public class TestAccountInstallation extends TestCase @@ -241,6 +242,20 @@ public class TestAccountInstallation } } + String xCapServerUri = System.getProperty(accountPrefix + + SipProtocolProviderServiceLick.XCAP_SERVER_PROPERTY_NAME, null); + if (xCapServerUri != null) + { + table.put(ProtocolProviderServiceSipImpl.XCAP_ENABLE, + Boolean.TRUE.toString()); + table.put(ProtocolProviderServiceSipImpl.XCAP_USE_SIP_CREDETIALS, + Boolean.TRUE.toString()); + table.put(ProtocolProviderServiceSipImpl.XCAP_USE_SIP_CREDETIALS, + Boolean.TRUE.toString()); + table.put(ProtocolProviderServiceSipImpl.XCAP_SERVER_URI, + xCapServerUri); + } + table.put(ProtocolProviderFactory.FORCE_P2P_MODE, Boolean.FALSE.toString()); diff --git a/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetServerStoredInfo.java b/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetServerStoredInfo.java new file mode 100644 index 0000000..4aa9631 --- /dev/null +++ b/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetServerStoredInfo.java @@ -0,0 +1,356 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.slick.protocol.sip; + +import junit.framework.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.impl.protocol.sip.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent.*; + +import java.net.URI; +import java.util.*; + +/** + * Testing of the user and account info. Tests for reading, adding, removing, + * replacing and error handling. + * + * @author Grigorii Balutsel + */ +public class TestOperationSetServerStoredInfo extends TestCase +{ + /** + * Fixture for testing. + */ + private SipSlickFixture fixture = new SipSlickFixture(); + + /** + * Account Info Operation set for testing. + */ + private OperationSetServerStoredAccountInfo + opSetServerStoredAccountInfo = null; + + /** + * XCAP client for testing. + */ + private XCapClient xCapClient; + + /** + * Creates tests under specific name. + * + * @param name the tests name. + */ + public TestOperationSetServerStoredInfo(String name) + { + super(name); + } + + /** + * Get a reference to the account info operation sets. + * + * @throws Exception + */ + protected void setUp() throws Exception + { + super.setUp(); + fixture.setUp(); + Map<String, OperationSet> supportedOperationSets = + fixture.provider1.getSupportedOperationSets(); + if (supportedOperationSets == null || supportedOperationSets.size() < 1) + { + throw new NullPointerException( + "No OperationSet implementations are supported by this " + + "SIP implementation."); + } + opSetServerStoredAccountInfo = (OperationSetServerStoredAccountInfo) + supportedOperationSets.get( + OperationSetServerStoredAccountInfo.class.getName()); + if (opSetServerStoredAccountInfo == null) + { + throw new NullPointerException( + "No implementation for Account Info was found"); + } + if (!opSetServerStoredAccountInfo + .isDetailClassSupported(ServerStoredDetails.ImageDetail.class)) + { + throw new NullPointerException( + "OperationSet does't support avatars"); + } + + // Connect to the XCAP server + xCapClient = createXCapClient(); + if (!xCapClient.isConnected()) + { + throw new NullPointerException("XCAP client is not connected"); + } + if (!xCapClient.isPresContentSupported()) + { + throw new NullPointerException( + "XCAP server doesn't support pres-content"); + } + + // Clean details + List<ServerStoredDetails.GenericDetail> details = + new ArrayList<ServerStoredDetails.GenericDetail>(); + Iterator<ServerStoredDetails.GenericDetail> detailIterator = + opSetServerStoredAccountInfo.getAllAvailableDetails(); + while (detailIterator.hasNext()) + { + details.add(detailIterator.next()); + } + for (ServerStoredDetails.GenericDetail detail : details) + { + opSetServerStoredAccountInfo.removeDetail(detail); + } + } + + protected void tearDown() throws Exception + { + super.tearDown(); + fixture.tearDown(); + xCapClient.dicsonnect(); + } + + /** + * Creates a test suite containing tests of this class in a specific order. + * We'll first execute tests beginning with the "test" prefix and then go to + * ordered tests. We first execute tests for reading info, then writing. + * Then the ordered tests - error handling and finaly for removing details + * + * @return Test a testsuite containing all tests to execute. + */ + public static Test suite() + { + if(System.getProperty(SipProtocolProviderServiceLick.ACCOUNT_1_PREFIX + + SipProtocolProviderServiceLick.XCAP_SERVER_PROPERTY_NAME) != null) + { + return new TestSuite(TestOperationSetServerStoredInfo.class); + } + return new TestSuite(); + } + + private XCapClient createXCapClient() + throws Exception + { + String userName = System.getProperty( + SipProtocolProviderServiceLick.ACCOUNT_1_PREFIX + + ProtocolProviderFactory.USER_ID); + String password = System.getProperty( + SipProtocolProviderServiceLick.ACCOUNT_1_PREFIX + + ProtocolProviderFactory.PASSWORD); + String xCapServerUri = System.getProperty( + SipProtocolProviderServiceLick.ACCOUNT_1_PREFIX + + SipProtocolProviderServiceLick + .XCAP_SERVER_PROPERTY_NAME); + XCapClient xCapClient = new XCapClientImpl(); + xCapClient.connect(new URI(xCapServerUri), + ((ProtocolProviderServiceSipImpl) fixture.provider1). + parseAddressString(userName), password); + return xCapClient; + } + + /** + * Tests reading info. Puts the image to the server by using XCAP client and + * then gets it by using Sip Communicator interfaces. + * + * @throws Exception if there is some error during test. + */ + public void testReadInfo() throws Exception + { + // Add image + byte[] imageContent = + TestOperationSetServerStoredInfoData.IMAGE_CONTENT_1; + ServerStoredDetails.ImageDetail imageDetail1 = + new ServerStoredDetails.ImageDetail(null, imageContent); + opSetServerStoredAccountInfo.addDetail(imageDetail1); + // Get saved image + Iterator<ServerStoredDetails.GenericDetail> storedDetails = + opSetServerStoredAccountInfo + .getDetails(ServerStoredDetails.ImageDetail.class); + assertNotNull("Stored details cannot be null", storedDetails); + assertTrue("Stored details doesn't have ImageDetail", + storedDetails.hasNext()); + ServerStoredDetails.GenericDetail storedImageDetail = + storedDetails.next(); + assertTrue("Stored details is not ImageDetail", + storedImageDetail instanceof ServerStoredDetails.ImageDetail); + byte[] savedContent = + ((ServerStoredDetails.ImageDetail) storedImageDetail).getBytes(); + assertEquals( + "The ImageDetail we set is not set or not read properly", + imageContent.length, + savedContent.length); + for (int i = 0; i < imageContent.length; i++) + { + assertSame("The ImageDetail we set has not the same content", + imageContent[i], savedContent[i]); + } + // Get pres-content from the server + ContentType presContent = xCapClient.getPresContent( + ProtocolProviderServiceSipImpl.PRES_CONTENT_IMAGE_NAME); + assertNotNull("Pres-content cannot be null", presContent); + assertNotNull("Pres-content data cannot be null", + presContent.getData()); + assertNotNull("Pres-content data value cannot be null", + presContent.getData().getValue()); + byte[] serverContent = Base64.decode(presContent.getData().getValue()); + assertEquals( + "The ImageDetail we set is not set or not read properly", + imageContent.length, serverContent.length); + for (int i = 0; i < imageContent.length; i++) + { + assertSame("The ImageDetail we set has not the same content", + imageContent[i], serverContent[i]); + } + // Create pres-content +// ContentType presContent = new ContentType(); +// ContentType.MimeType mimeType = new ContentType.MimeType(); +// mimeType.setValue(TestOperationSetServerStoredInfoData.IMAGE_TYPE); +// presContent.setMimeType(mimeType); +// ContentType.EncodingType encoding = new ContentType.EncodingType(); +// encoding.setValue("base64"); +// presContent.setEncoding(encoding); +// ContentType.DataType data = new ContentType.DataType(); +// data.setValue(encodedImageContent); +// presContent.setData(data); + // Put pres-content to the server +// xCapClient.putPresContent(presContent, +// ProtocolProviderServiceSipImpl.PRES_CONTENT_IMAGE_NAME); + } + + /** + * Tests writing info. Puts the image to the server by using Sip + * Communicator interfaces and then gets it by using XCAP client. + * + * @throws Exception if there is some error during test. + */ + public void testWriteInfo() throws Exception + { + byte[] imageContent = + TestOperationSetServerStoredInfoData.IMAGE_CONTENT_1; + ServerStoredDetails.ImageDetail imageDetail = + new ServerStoredDetails.ImageDetail(null, imageContent); + opSetServerStoredAccountInfo.addDetail(imageDetail); + // Get pres-content + ContentType presContent = xCapClient.getPresContent( + ProtocolProviderServiceSipImpl.PRES_CONTENT_IMAGE_NAME); + assertNotNull("Pres-content cannot be null", presContent); + assertNotNull("Pres-content data cannot be null", + presContent.getData()); + assertNotNull("Pres-content data value cannot be null", + presContent.getData().getValue()); + byte[] serverContent = Base64.decode(presContent.getData().getValue()); + assertEquals( + "The ImageDetail we set is not set or not read properly", + imageContent.length, serverContent.length); + for (int i = 0; i < imageContent.length; i++) + { + assertSame("The ImageDetail we set has not the same content", + imageContent[i], serverContent[i]); + } + // Remove saved image + opSetServerStoredAccountInfo.removeDetail(imageDetail); + } + + /** + * Tests deleting info. Puts the image to the server by using Sip + * Communicator interfaces, deletes it and then gets it by using XCAP + * client. + * + * @throws Exception if there is some error during test. + */ + public void testRemoveInfo() throws Exception + { + byte[] imageContent = + TestOperationSetServerStoredInfoData.IMAGE_CONTENT_1; + ServerStoredDetails.ImageDetail imageDetail = + new ServerStoredDetails.ImageDetail(null, imageContent); + opSetServerStoredAccountInfo.addDetail(imageDetail); + // Remove saved image + boolean removeResult = + opSetServerStoredAccountInfo.removeDetail(imageDetail); + assertTrue("The result of remove operation cannot be false", + removeResult); + // Get saved image + Iterator<ServerStoredDetails.GenericDetail> storedDetails = + opSetServerStoredAccountInfo + .getDetails(ServerStoredDetails.ImageDetail.class); + assertNotNull("Stored details cannot be null", storedDetails); + assertFalse("Stored details cannot have ImageDetail", + storedDetails.hasNext()); + // Get pres-content + ContentType presContent = xCapClient.getPresContent( + ProtocolProviderServiceSipImpl.PRES_CONTENT_IMAGE_NAME); + assertNull("Pres-content cannot be not null", presContent); + } + + /** + * Tests replacing info. Puts the image to the server by using Sip + * Communicator interfaces, replace it and then gets it by using XCAP + * client. + * + * @throws Exception if there is some error during test. + */ + public void testReplaceInfo() throws Exception + { + byte[] imageContent1 = + TestOperationSetServerStoredInfoData.IMAGE_CONTENT_1; + byte[] imageContent2 = + TestOperationSetServerStoredInfoData.IMAGE_CONTENT_2; + ServerStoredDetails.ImageDetail imageDetail1 = + new ServerStoredDetails.ImageDetail(null, imageContent1); + ServerStoredDetails.ImageDetail imageDetail2 = + new ServerStoredDetails.ImageDetail(null, imageContent2); + opSetServerStoredAccountInfo.addDetail(imageDetail1); + boolean replaceResult = opSetServerStoredAccountInfo + .replaceDetail(imageDetail1, imageDetail2); + assertTrue("The result of replace operation cannot be false", + replaceResult); + // Get saved image + Iterator<ServerStoredDetails.GenericDetail> storedDetails = + opSetServerStoredAccountInfo + .getDetails(ServerStoredDetails.ImageDetail.class); + assertNotNull("Stored details cannot be null", storedDetails); + assertTrue("Stored details doesn't have ImageDetail", + storedDetails.hasNext()); + ServerStoredDetails.GenericDetail imageDetail = + storedDetails.next(); + assertTrue("Stored details is not ImageDetail", + imageDetail instanceof ServerStoredDetails.ImageDetail); + byte[] savedContent = + ((ServerStoredDetails.ImageDetail) imageDetail).getBytes(); + assertEquals( + "The ImageDetail we set is not set or not read properly", + imageContent2.length, + savedContent.length); + for (int i = 0; i < imageContent2.length; i++) + { + assertSame("The ImageDetail we set has not the same content", + imageContent2[i], savedContent[i]); + } + // Get pres-content + ContentType presContent = xCapClient.getPresContent( + ProtocolProviderServiceSipImpl.PRES_CONTENT_IMAGE_NAME); + assertNotNull("Pres-content cannot be null", presContent); + assertNotNull("Pres-content data cannot be null", + presContent.getData()); + assertNotNull("Pres-content data value cannot be null", + presContent.getData().getValue()); + byte[] serverContent = Base64.decode(presContent.getData().getValue()); + assertEquals( + "The ImageDetail we set is not set or not read properly", + imageContent2.length, serverContent.length); + for (int i = 0; i < imageContent2.length; i++) + { + assertSame("The ImageDetail we set has not the same content", + imageContent2[i], serverContent[i]); + } + // Remove saved image + opSetServerStoredAccountInfo.removeDetail(imageDetail2); + } +} diff --git a/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetServerStoredInfoData.java b/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetServerStoredInfoData.java new file mode 100644 index 0000000..794d81b --- /dev/null +++ b/test/net/java/sip/communicator/slick/protocol/sip/TestOperationSetServerStoredInfoData.java @@ -0,0 +1,175 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.slick.protocol.sip; + +/** + * Contains data for server stored info tests. + * + * @author Grigorii Balutsel + */ +public class TestOperationSetServerStoredInfoData +{ + /** + * The image content. + */ + public static String IMAGE_TYPE = "image/png"; + + /** + * The image content. + */ + public static byte[] IMAGE_CONTENT_1 = new byte[]{ + -119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, + 1, 0, 0, 0, 1, 0, 8, 2, 0, 0, 0, -45, 16, 63, 49, 0, 0, 0, 1, 115, + 82, 71, 66, 0, -82, -50, 28, -23, 0, 0, 0, 4, 103, 65, 77, 65, 0, 0, + -79, -113, 11, -4, 97, 5, 0, 0, 0, 32, 99, 72, 82, 77, 0, 0, 122, + 38, 0, 0, -128, -124, 0, 0, -6, 0, 0, 0, -128, -24, 0, 0, 117, 48, + 0, 0, -22, 96, 0, 0, 58, -104, 0, 0, 23, 112, -100, -70, 81, 60, 0, + 0, 3, 114, 73, 68, 65, 84, 120, 94, -19, -48, 49, 1, 0, 0, 0, -62, + -96, -11, 79, -19, 101, 11, -120, 64, 97, -64, -128, 1, 3, 6, 12, + 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, + 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, + 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, + 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, + 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, + -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, + -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, + 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, + 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, + 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, + 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, + 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, + 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, + 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, + -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, + -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, + 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, + 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, + 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, + 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, + 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, + 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, + 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, + -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, + -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, + 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, + 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, + 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, + 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, + 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, + 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, + 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, + -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, + -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, + 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, + 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, + 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, + 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, + 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, + 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, + 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, + -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, + -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, + 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, + 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, + 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, + 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, + 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, + 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, -128, + 1, 3, 6, 12, 24, 48, 96, -64, -128, 1, 3, 6, 12, 24, 48, 96, -64, + -64, 7, 6, 1, 45, 0, 1, 96, 53, 25, 121, 0, 0, 0, 0, 73, 69, 78, 68, + -82, 66, 96, -126 + }; + + /** + * The image content. + */ + public static byte[] IMAGE_CONTENT_2 = new byte[]{ + -119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, + 1, 0, 0, 0, 1, 0, 8, 2, 0, 0, 0, -45, 16, 63, 49, 0, 0, 0, 4, 103, + 65, 77, 65, 0, 0, -79, -113, 11, -4, 97, 5, 0, 0, 4, -64, 73, 68, + 65, 84, 120, 94, -19, -44, -63, 13, 0, 32, 12, 3, 49, -70, -1, -48, + -64, -85, 75, -100, 17, 11, -60, 106, 50, -9, 120, 4, -62, 2, -65, + 0, 62, -127, -84, -128, -21, 39, -112, 22, 72, -121, -49, -50, -98, + -32, 43, -96, 0, 4, -46, 2, -23, -16, -122, -112, -128, 2, 16, 72, + 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, + 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, + -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, + -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, + 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, + 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, + 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, + 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, + 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, + -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, + 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, + -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, + -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, + -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, + -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, + 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, + 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, + 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, + -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, + -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, + -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, + -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, + -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, + 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, + 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, + 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, + 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, + 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, + -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, + 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, + -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, + -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, + -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, + -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, + 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, + 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, + 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, + -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, + -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, + -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, + -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, + -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, + 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, + 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, + 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, + 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, + 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, + -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, + 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, + -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, + -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, + -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, + -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, + 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, + 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, + 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, + -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, + -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, + -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, + -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, + -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, + 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, + 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, + 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, + 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, + 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, + -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, + 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, + -96, 0, 4, -46, 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, + -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, 111, -1, 8, 40, 0, -127, + -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, 2, -23, -16, -10, -113, + -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, 10, 64, 32, 45, -112, 14, + 111, -1, 8, 40, 0, -127, -76, 64, 58, -68, -3, 35, -96, 0, 4, -46, + 2, -23, -16, -10, -113, -128, 2, 16, 72, 11, -92, -61, -37, 63, 2, + 15, -73, -86, 16, -29, 81, -81, -102, 105, 0, 0, 0, 0, 73, 69, 78, + 68, -82, 66, 96, -126 + }; +} diff --git a/test/net/java/sip/communicator/slick/protocol/sip/TestXCapParse.java b/test/net/java/sip/communicator/slick/protocol/sip/TestXCapParse.java new file mode 100644 index 0000000..da04692 --- /dev/null +++ b/test/net/java/sip/communicator/slick/protocol/sip/TestXCapParse.java @@ -0,0 +1,503 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.slick.protocol.sip; + +import junit.framework.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent.*; +import net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror.*; +import org.w3c.dom.*; + +import javax.xml.namespace.*; + +/** + * Contains tests of parsing xcap-caps, resource-lists, pres-content. + * + * @author Grigorii Balutsel + */ +public class TestXCapParse extends TestCase +{ + /** + * The resource-xml for the tests. + */ + private static String RESOURCE_LISTS_XML = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + + "<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\"" + + " xmlns:ext=\"extension\"" + + " xmlns:xsi=\"htt//www.w3.org/2001/XMLSchema-instance\">" + + " xsi:schemaLocation=\"resource-lists.xsd \">" + + " <list name=\"list1\" ext:name=\"ext list1\">" + + " <entry uri=\"sip:entry1@example.com\"" + + " ext:uri=\"sip:user1@example.com\">" + + " <display-name>Entry1</display-name>" + + " <ext:display-name>Ext:Entry1</ext:display-name>" + + " </entry>" + + " <entry uri=\"sip:entry2@example.com\">" + + " <display-name xml:lang=\"en-US\">Entry2</display-name>" + + " </entry>" + + " <list name=\"sub_group1\"/>" + + " <external anchor=\"anchor_uri\">" + + " <display-name xml:lang=\"en-US\">External</display-name>" + + " </external>" + + " <entry-ref ref=\"ref_uri\">" + + " <display-name xml:lang=\"en-US\">Entry1</display-name>" + + " </entry-ref>" + + " <ext:entry uri=\"sip:user1@example.com\" " + + " ext:uri=\"sip:user1@example.com\"/>" + + " </list>" + + " <list name=\"list2\">" + + " <display-name xml:lang=\"en-US\">List2</display-name>" + + " </list>" + + "</resource-lists>"; + + /** + * The resource-xml for the tests. + */ + private static String XCAP_CAPS_XML = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + + "<xcap-caps xmlns=\"urn:ietf:params:xml:ns:xcap-caps\"" + + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + + " xsi:schemaLocation=\"xcap-caps.xsd \">" + + " <auids>" + + " <auid>resource-lists</auid>" + + " <auid>rls-services</auid>" + + " </auids>" + + " <extensions>" + + " <!-- No extensions defined -->" + + " </extensions>" + + " <namespaces>" + + " <namespace>urn:ietf:params:xml:ns:xcap-caps</namespace>" + + " <namespace>urn:ietf:params:xml:ns:xcap-error</namespace>" + + " <namespace>urn:ietf:params:xml:ns:resource-lists</namespace>" + + " </namespaces>" + + "</xcap-caps>"; + + /** + * The pres-content for the tests. + */ + private static String PRES_CONTENT_XML = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + + "<content xmlns=\"urn:oma:xml:prs:pres-content\">" + + " <mime-type>image/png</mime-type>" + + " <encoding>base64</encoding>" + + " <description>Description</description>" + + " <description xml:lang=\"en-US\">Description</description>" + + " <data>Data</data>" + + "</content>"; + + /** + * The xcap-error uniqueness-failure for the tests. + */ + private static String XCAP_ERROR_CANNOT_DELETE_XML = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + + "<xcap-error xmlns=\"urn:ietf:params:xml:ns:xcap-error\">" + + " <cannot-delete phrase=\"Cannot Delete\"/>" + + "</xcap-error>"; + + /** + * The xcap-error uniqueness-failure for the tests. + */ + private static String XCAP_ERROR_UNIQUENESS_FAILURE_XML = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + + "<xcap-error xmlns=\"urn:ietf:params:xml:ns:xcap-error\">" + + " <uniqueness-failure>" + + " <exists field=\"field\">" + + " <alt-value>sip:entry@example.com</alt-value>" + + " </exists>" + + " </uniqueness-failure>" + + "</xcap-error>"; + + /** + * Creates a test suite containing tests of this class in a specific order. + * We'll first execute tests beginning with the "test" prefix and then go to + * ordered tests. We first execute tests for reading info, then writing. + * Then the ordered tests - error handling and finaly for removing details + * + * @return Test a testsuite containing all tests to execute. + */ + public static Test suite() + { + return new TestSuite(TestXCapParse.class); + } + + /** + * Tests resource-lists parsing. + * + * @throws Exception if there is some error during test. + */ + public static void testResourceListsParse() throws Exception + { + ResourceListsType originalResourceLists = + ResourceListsParser.fromXml(RESOURCE_LISTS_XML); + validateResourceLists(originalResourceLists); + String xml = ResourceListsParser.toXml(originalResourceLists); + ResourceListsType storedResourceLists = + ResourceListsParser.fromXml(xml); + validateResourceLists(storedResourceLists); + } + + /** + * Tests xcap-caps parsing. + * + * @throws Exception if there is some error during test. + */ + public static void testXCapCapsParse() throws Exception + { + XCapCapsType originalXCapCaps = + XCapCapsParser.fromXml(XCAP_CAPS_XML); + validateXCapCaps(originalXCapCaps); + } + + /** + * Tests pres-content parsing. + * + * @throws Exception if there is some error during test. + */ + public static void testPresContentParse() throws Exception + { + ContentType originalContent = + PresContentParser.fromXml(PRES_CONTENT_XML); + validatePresContent(originalContent); + String xml = PresContentParser.toXml(originalContent); + ContentType storedContent = PresContentParser.fromXml(xml); + validatePresContent(storedContent); + } + + /** + * Tests xcap-error parsing. + * + * @throws Exception if there is some error during test. + */ + public static void testXCapErrorParse() throws Exception + { + XCapErrorType cannotDelete = + XCapErrorParser.fromXml(XCAP_ERROR_CANNOT_DELETE_XML); + validateXCapErrorConnotDelete(cannotDelete); + + XCapErrorType uniquenessFailure = + XCapErrorParser.fromXml(XCAP_ERROR_UNIQUENESS_FAILURE_XML); + validateXCapErrorUniquenessFailure(uniquenessFailure); + } + + /** + * Validates uniqueness-failure with the original XML. + * + * @param xCapError the xcap-error to analyze. + */ + private static void validateXCapErrorUniquenessFailure( + XCapErrorType xCapError) + { + assertNotNull("xcap-error cannot be null", xCapError); + assertNotNull("uniqueness-failure cannot be null", + xCapError.getError()); + assertTrue( + "The uniqueness-failure elements we set is not read properly", + xCapError.getError() instanceof UniquenessFailureType); + UniquenessFailureType uniquenessFailure = (UniquenessFailureType) + xCapError.getError(); + assertNull("The phrase we set is not read properly", + uniquenessFailure.getPhrase()); + assertTrue("The exists elements we set is not read properly", + uniquenessFailure.getExists().size() == 1); + UniquenessFailureType.ExistsType exists1 = + uniquenessFailure.getExists().get(0); + assertEquals("The exists[0] element we set is not read properly", + exists1.getField(), "field"); + assertTrue("The exists[0]altValue we set is not read properly", + exists1.getAltValue().size() == 1); + assertEquals("The exists[0]altValue[0] we set is not read properly", + exists1.getAltValue().get(0), "sip:entry@example.com"); + } + + /** + * Validates cannot-delete with the original XML. + * + * @param xCapError the xcap-error to analyze. + */ + private static void validateXCapErrorConnotDelete( + XCapErrorType xCapError) + { + assertNotNull("xcap-error cannot be null", xCapError); + assertNotNull("cannot-delete cannot be null", + xCapError.getError()); + assertTrue( + "The cannot-delete elements we set is not read properly", + xCapError.getError() instanceof CannotDeleteType); + CannotDeleteType cannotDelete = (CannotDeleteType) + xCapError.getError(); + assertEquals("The phrase we set is not read properly", + cannotDelete.getPhrase(), "Cannot Delete"); + } + + /** + * Validates resource-lists with the original XML. + * + * @param resourceLists the resource-lists to analyze. + */ + private static void validateResourceLists(ResourceListsType resourceLists) + { + assertNotNull("resource-lists cannot be null", resourceLists); + assertTrue("The first level lists we set is not read properly", + resourceLists.getList().size() == 2); + // list1 + ListType list1 = resourceLists.getList().get(0); + assertEquals( + "The lists[1] name we set is not read properly", + list1.getName(), "list1"); + assertEquals( + "The lists[1] name we set is not read properly", + list1.getName(), "list1"); + assertNull("The lists[1] display-name we set is not read properly", + list1.getDisplayName()); + assertTrue("The lists[1] entries we set is not read properly", + list1.getEntries().size() == 2); + assertTrue("The lists[1] lists we set is not read properly", + list1.getLists().size() == 1); + assertTrue("The lists[1] externals we set is not read properly", + list1.getExternals().size() == 1); + assertTrue("The lists[1] entryRefs we set is not read properly", + list1.getEntryRefs().size() == 1); + assertTrue("The lists[1] custom attriutes we set is not read properly", + list1.getAnyAttributes().size() == 1); + assertTrue("The lists[1] custom elements we set is not read properly", + list1.getAnyAttributes().size() == 1); + + String list1ExtAttribute = list1.getAnyAttributes().get( + new QName("extension", "name", "ext")); + assertNotNull( + "The lists[1]ext:display-name attribute we set is not read " + + "properly", + list1ExtAttribute); + assertEquals( + "The lists[1]ext:display-name attribute we set is not read " + + "properly", + list1ExtAttribute, "ext list1"); + + Element list1ExtElement = list1.getAny().get(0); + assertEquals( + "The lists[1]ext:entry attribute we set is not read " + + "properly", + list1ExtElement.getLocalName(), "entry"); + assertEquals( + "The lists[1]ext:entry attribute we set is not read " + + "properly", + XmlUtils.getNamespaceUri(list1ExtElement), "extension"); + assertEquals( + "The lists[1]ext:entry attribute we set is not read " + + "properly", + list1ExtElement.getPrefix(), "ext"); + + EntryType lis1Entry1 = list1.getEntries().get(0); + assertNotNull( + "The lists[1]entry[1] name we set is not read properly", + lis1Entry1.getUri()); + assertEquals( + "The lists[1]entry[1] name we set is not read properly", + lis1Entry1.getUri(), "sip:entry1@example.com"); + assertNotNull( + "The lists[1]entry[1] display-name we set is not read properly", + lis1Entry1.getDisplayName()); + assertEquals( + "The lists[1]entry[1] display-name we set is not read properly", + lis1Entry1.getDisplayName().getValue(), "Entry1"); + assertNull( + "The lists[1]entry[1] display-name we set is not read properly", + lis1Entry1.getDisplayName().getLang()); + String enty1ExtAttribute = lis1Entry1.getAnyAttributes().get( + new QName("extension", "uri", "ext")); + assertNotNull( + "The lists[1]entry[1]ext:uri attribute we set is not read " + + "properly", + enty1ExtAttribute); + assertEquals( + "The lists[1]entry[1]ext:uri attribute we set is not read " + + "properly", + enty1ExtAttribute, "sip:user1@example.com"); + Element enty1ExtElement = lis1Entry1.getAny().get(0); + assertEquals( + "The lists[1]entry[1]ext:entry element we set is not read " + + "properly", + enty1ExtElement.getLocalName(), "display-name"); + assertEquals( + "The lists[1]entry[1]ext:dispaly-name element we set is not " + + "read properly", + XmlUtils.getNamespaceUri(enty1ExtElement), "extension"); + assertEquals( + "The lists[1]entry[1]ext:dispaly-name element we set is not " + + "read properly", + enty1ExtElement.getPrefix(), "ext"); + assertEquals( + "The lists[1]entry[1]ext:dispaly-name element we set is not " + + "read properly", + enty1ExtElement.getTextContent(), "Ext:Entry1"); + + EntryType lis1Entry2 = list1.getEntries().get(1); + assertNotNull( + "The lists[1]entry[2] display-name we set is not read properly", + lis1Entry2.getDisplayName()); + assertNotNull( + "The lists[1]entry[2] name we set is not read properly", + lis1Entry2.getUri()); + assertEquals( + "The lists[1]entry[2] name we set is not read properly", + lis1Entry2.getUri(), "sip:entry2@example.com"); + assertEquals( + "The lists[1]entry[2] display-name we set is not read properly", + lis1Entry2.getDisplayName().getValue(), "Entry2"); + assertEquals( + "The lists[1]entry[2] display-name we set is not read properly", + lis1Entry2.getDisplayName().getLang(), "en-US"); + + EntryRefType list1EntryRef = list1.getEntryRefs().get(0); + assertEquals( + "The lists[1]entryRef[1] name we set is not read properly", + list1EntryRef.getRef(), "ref_uri"); + assertNotNull( + "The lists[1]entryRef[1] display-name we set is not read " + + "properly", + list1EntryRef.getDisplayName()); + assertEquals( + "The llists[1]entryRef[1] display-name we set is not read " + + "properly", + list1EntryRef.getDisplayName().getValue(), "Entry1"); + assertEquals( + "The lists[1]entryRef[1]display-name we set is not read " + + "properly", + list1EntryRef.getDisplayName().getLang(), "en-US"); + + ExternalType list1External = list1.getExternals().get(0); + assertEquals( + "The lists[1]external[1] name we set is not read properly", + list1External.getAnchor(), "anchor_uri"); + assertNotNull( + "The lists[1]external[1] display-name we set is not read " + + "properly", + list1External.getDisplayName()); + assertEquals( + "The llists[1]external[1] display-name we set is not read " + + "properly", + list1External.getDisplayName().getValue(), "External"); + assertEquals( + "The lists[1]external[1]display-name we set is not read " + + "properly", + list1External.getDisplayName().getLang(), "en-US"); + // list2 + ListType list2 = resourceLists.getList().get(1); + assertEquals( + "The lists[2] name we set is not read properly", + list2.getName(), "list2"); + assertNotNull("The lists[2] display-name we set is not read properly", + list2.getDisplayName()); + assertEquals("The lists[2] display-name we set is not read properly", + list2.getDisplayName().getValue(), "List2"); + assertEquals("The lists[2] display-name we set is not read properly", + list2.getDisplayName().getLang(), "en-US"); + } + + /** + * Validates xcap-caps with the original XML. + * + * @param xCapCaps the xcap-caps to analyze. + */ + private static void validateXCapCaps(XCapCapsType xCapCaps) + { + assertNotNull("xcap-caps cannot be null", xCapCaps); + + AuidsType auids = xCapCaps.getAuids(); + NamespacesType namespaces = xCapCaps.getNamespaces(); + ExtensionsType extensions = xCapCaps.getExtensions(); + + assertNotNull("The auids we set is not read properly", auids); + assertTrue("The auids we set is not read properly", + auids.getAuid().size() == 2); + assertNotNull("The namespaces we set is not read properly", namespaces); + assertTrue("The namespaces we set is not read properly", + namespaces.getNamespace().size() == 3); + assertNotNull("The auids we set is not read properly", extensions); + assertTrue("The extensions we set is not read properly", + extensions.getExtension().size() == 0); + // auids + assertEquals( + "The auids[0] name we set is not read properly", + auids.getAuid().get(0), "resource-lists"); + assertEquals( + "The auids[1] name we set is not read properly", + auids.getAuid().get(1), "rls-services"); + // namespaces + assertEquals( + "The namespaces[0] name we set is not read properly", + namespaces.getNamespace().get(0), + "urn:ietf:params:xml:ns:xcap-caps"); + assertEquals( + "The namespaces[1] name we set is not read properly", + namespaces.getNamespace().get(1), + "urn:ietf:params:xml:ns:xcap-error"); + assertEquals( + "The namespaces[2] name we set is not read properly", + namespaces.getNamespace().get(2), + "urn:ietf:params:xml:ns:resource-lists"); + } + + /** + * Validates pres-content with the original XML. + * + * @param presContent the pres-content to analyze. + */ + private static void validatePresContent(ContentType presContent) + { + assertNotNull("pres-content cannot be null", presContent); + DataType data = presContent.getData(); + MimeType mimeType = presContent.getMimeType(); + EncodingType encoding = presContent.getEncoding(); + // data + assertNotNull("The data we set is not read properly", data); + assertEquals("The data we set is not read properly", + data.getValue(), "Data"); + assertTrue("The data custom elements we set is not read properly", + data.getAnyAttributes().size() == 0); + // mime-type + assertNotNull("The mime-type we set is not read properly", mimeType); + assertEquals("The mime-type we set is not read properly", + mimeType.getValue(), "image/png"); + assertTrue("The mime-type custom elements we set is not read properly", + mimeType.getAnyAttributes().size() == 0); + // encoding + assertNotNull("The encoding we set is not read properly", encoding); + assertEquals("The encoding we set is not read properly", + encoding.getValue(), "base64"); + assertTrue("The encoding custom elements we set is not read properly", + encoding.getAnyAttributes().size() == 0); + // description + assertNotNull("The description we set is not read properly", + presContent.getDescription()); + assertTrue("The description we set is not read properly", + presContent.getDescription().size() == 2); + DescriptionType description1 = presContent.getDescription().get(0); + assertEquals( + "The description[0] we set is not read properly", + description1.getValue(), "Description"); + assertNull( + "The description[0] display-name we set is not read properly", + description1.getLang()); + assertTrue( + "The description[0] custom elements we set is not read properly", + description1.getAnyAttributes().size() == 0); + + DescriptionType description2 = presContent.getDescription().get(1); + assertEquals( + "The description[1] we set is not read properly", + description2.getValue(), "Description"); + assertEquals( + "The description[1] we set is not read properly", + description2.getLang(), "en-US"); + assertTrue( + "The description[1] custom elements we set is not read properly", + description2.getAnyAttributes().size() == 0); + } +} diff --git a/test/net/java/sip/communicator/slick/protocol/sip/sip.provider.slick.manifest.mf b/test/net/java/sip/communicator/slick/protocol/sip/sip.provider.slick.manifest.mf index d285dec..85b6286 100644 --- a/test/net/java/sip/communicator/slick/protocol/sip/sip.provider.slick.manifest.mf +++ b/test/net/java/sip/communicator/slick/protocol/sip/sip.provider.slick.manifest.mf @@ -8,7 +8,16 @@ Import-Package: net.java.sip.communicator.service.configuration, net.java.sip.communicator.service.configuration.event, junit.framework, org.osgi.framework, + org.w3c.dom; + javax.xml.namespace; net.java.sip.communicator.util, net.java.sip.communicator.service.protocol, - net.java.sip.communicator.service.protocol.event - + net.java.sip.communicator.service.protocol.event, + net.java.sip.communicator.impl.protocol.sip, + net.java.sip.communicator.impl.protocol.sip.xcap, + net.java.sip.communicator.impl.protocol.sip.xcap.utils, + net.java.sip.communicator.impl.protocol.sip.xcap.model, + net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps, + net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists, + net.java.sip.communicator.impl.protocol.sip.xcap.model.prescontent, + net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror |