From 11d8d7f42985d7bcc03e0b9b281bc721acee2277 Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov Date: Fri, 23 Mar 2012 13:23:10 +0000 Subject: [PATCH] Implements IQ and IQProvider for the cobri Jabber extension. --- .../ProtocolProviderServiceJabberImpl.java | 126 ++++- .../extensions/cobri/CobriConferenceIQ.java | 517 ++++++++++++++++++ .../extensions/cobri/CobriIQProvider.java | 187 +++++++ 3 files changed, 801 insertions(+), 29 deletions(-) create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriConferenceIQ.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriIQProvider.java diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java index 10d6ebced..127b5a744 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java @@ -15,6 +15,8 @@ import javax.net.ssl.*; import net.java.sip.communicator.impl.protocol.jabber.debugger.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.cobri.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.keepalive.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; @@ -209,7 +211,7 @@ public class ProtocolProviderServiceJabberImpl /** * Used to connect to a XMPP server. */ - private XMPPConnection connection = null; + private XMPPConnection connection; /** * Indicates whether or not the provider is initialized and ready for use. @@ -449,9 +451,9 @@ public void register(final SecurityAuthority authority) { // Checks if an error has occurred during login, if so we fire // it here in order to avoid a deadlock which occurs in - // reconnect plugin. The deadlock is cause we fired an event during - // login process and have locked initializationLock and we cannot - // unregister from reconnect, cause unregister method + // reconnect plugin. The deadlock is cause we fired an event + // during login process and have locked initializationLock and + // we cannot unregister from reconnect, cause unregister method // also needs this lock. if(eventDuringLogin != null) { @@ -1578,9 +1580,9 @@ protected void initialize(String screenname, // RTP HDR extension supportedFeatures.add(URN_XMPP_JINGLE_RTP_HDREXT); - //register our jingle provider - //register our home grown Jingle Provider. ProviderManager providerManager = ProviderManager.getInstance(); + + //register our jingle provider providerManager.addIQProvider( JingleIQ.ELEMENT_NAME, JingleIQ.NAMESPACE, new JingleIQProvider()); @@ -1606,6 +1608,24 @@ protected void initialize(String screenname, JingleInfoQueryIQ.NAMESPACE, new JingleInfoQueryIQProvider()); + // Jitsi VideoBridge IQProvider and PacketExtensionProvider + providerManager.addIQProvider( + CobriConferenceIQ.ELEMENT_NAME, + CobriConferenceIQ.NAMESPACE, + new CobriIQProvider()); + providerManager.addExtensionProvider( + PayloadTypePacketExtension.ELEMENT_NAME, + CobriConferenceIQ.NAMESPACE, + new DefaultPacketExtensionProvider< + PayloadTypePacketExtension>( + PayloadTypePacketExtension.class)); + providerManager.addExtensionProvider( + ParameterPacketExtension.ELEMENT_NAME, + CobriConferenceIQ.NAMESPACE, + new DefaultPacketExtensionProvider< + ParameterPacketExtension>( + ParameterPacketExtension.class)); + //initialize the telephony operation set boolean isCallingDisabled = JabberActivator.getConfigurationService() @@ -2179,16 +2199,7 @@ public boolean isFeatureSupported(String jid, String feature) */ public String getFullJid(Contact contact) { - XMPPConnection connection = getConnection(); - if (connection == null) - return null; - - Roster roster = getConnection().getRoster(); - - if (roster == null) - return null; - - return roster.getPresence(contact.getAddress()).getFrom(); + return getFullJid(contact.getAddress()); } /** @@ -2203,13 +2214,15 @@ public String getFullJid(Contact contact) public String getFullJid(String bareJid) { XMPPConnection connection = getConnection(); - if (connection == null) - return null; - Roster roster = getConnection().getRoster(); - Presence presence = roster.getPresence(bareJid); + if (connection != null) + { + Roster roster = connection.getRoster(); - return presence.getFrom(); + if (roster != null) + return roster.getPresence(bareJid).getFrom(); + } + return null; } /** @@ -2325,17 +2338,16 @@ public String getOurJID() { String jid = null; - if( connection != null && connection.getUser() != null) - return connection.getUser(); + if (connection != null) + jid = connection.getUser(); if (jid == null) { - //seems like the connection is not yet initialized so lets try - //to construct our jid ourselves. - String userID = - StringUtils.parseName(getAccountID().getUserID()); - String serviceName = - StringUtils.parseServer(getAccountID().getUserID()); + // seems like the connection is not yet initialized so lets try to + // construct our jid ourselves. + String accountIDUserID = getAccountID().getUserID(); + String userID = StringUtils.parseName(accountIDUserID); + String serviceName = StringUtils.parseServer(accountIDUserID); jid = userID + "@" + serviceName; } @@ -2606,4 +2618,60 @@ private void setTrafficClass() } } } + + /** + * Gets the entity ID of the first Jitsi VideoBridge associated with + * {@link #connection} i.e. provided by the serviceName of + * connection. + * + * @return the entity ID of the first Jitsi VideoBridge associated with + * connection + */ + public String getJitsiVideoBridge() + { + XMPPConnection connection = getConnection(); + + if (connection != null) + { + ScServiceDiscoveryManager discoveryManager = getDiscoveryManager(); + String serviceName = connection.getServiceName(); + DiscoverItems discoverItems = null; + + try + { + discoverItems = discoveryManager.discoverItems(serviceName); + } + catch (XMPPException xmppe) + { + } + if (discoverItems != null) + { + Iterator discoverItemIter + = discoverItems.getItems(); + + while (discoverItemIter.hasNext()) + { + DiscoverItems.Item discoverItem = discoverItemIter.next(); + String entityID = discoverItem.getEntityID(); + DiscoverInfo discoverInfo = null; + + try + { + discoverInfo = discoveryManager.discoverInfo(entityID); + } + catch (XMPPException xmppe) + { + } + if ((discoverInfo != null) + && discoverInfo.containsFeature( + CobriConferenceIQ.NAMESPACE)) + { + return entityID; + } + } + } + } + + return null; + } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriConferenceIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriConferenceIQ.java new file mode 100644 index 000000000..e33bf4c7c --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriConferenceIQ.java @@ -0,0 +1,517 @@ +/* + * Jitsi, 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.jabber.extensions.cobri; + +import java.util.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; + +import org.jivesoftware.smack.packet.*; + +/** + * Implements the Jitsi VideoBridge conference IQ. + * + * @author Lyubomir Marinov + */ +public class CobriConferenceIQ + extends IQ +{ + /** + * The XML element name of the Jitsi VideoBridge conference IQ. + */ + public static final String ELEMENT_NAME = "conference"; + + /** + * The XML name of the id attribute of the Jitsi VideoBridge + * conference IQ which represents the value of the id + * property of CobriConferenceIQ. + */ + public static final String ID_ATTR_NAME = "id"; + + /** + * The XML namespace of the Jitsi VideoBridge conference IQ. + */ + public static final String NAMESPACE + = "http://jitsi.org/protocol/videobridge#conference"; + + /** + * The list of {@link Content}s included into this conference IQ. + */ + private final List contents = new LinkedList(); + + /** + * The ID of the conference represented by this IQ. + */ + private String id; + + /** + * Initializes a new {@link Content} instance with a specific name and adds + * it to the list of Content instances included into this + * conference IQ. + * + * @param contentName the name which which the new Content instance + * is to be initialized + * @return true if the list of Content instances included + * into this conference IQ has been modified as a result of the + * method call; otherwise, false + */ + public boolean addContent(String contentName) + { + return addContent(new Content(contentName)); + } + + /** + * Adds a specific {@link Content} instance to the list of Content + * instances included into this conference IQ. + * + * @param content the Content instance to be added to this list of + * Content instances included into this conference IQ + * @return true if the list of Content instances included + * into this conference IQ has been modified as a result of the + * method call; otherwise, false + * @throws NullPointerException if the specified content is + * null + */ + public boolean addContent(Content content) + { + if (content == null) + throw new NullPointerException("content"); + + return contents.contains(content) ? false : contents.add(content); + } + + public String getChildElementXML() + { + StringBuilder xml = new StringBuilder(); + + xml.append('<').append(ELEMENT_NAME); + xml.append(" xmlns='").append(NAMESPACE).append('\''); + + String id = getID(); + + if (id != null) + xml.append(' ').append(ID_ATTR_NAME).append("='").append(id) + .append('\''); + + List contents = getContents(); + + if (contents.size() == 0) + { + xml.append(" />"); + } + else + { + xml.append('>'); + for (Content content : contents) + content.toXML(xml); + xml.append("'); + } + return xml.toString(); + } + + public Content getContent(String contentName) + { + for (Content content : getContents()) + if (contentName.equals(content.getName())) + return content; + return null; + } + + public List getContents() + { + return Collections.unmodifiableList(contents); + } + + /** + * Gets the ID of the conference represented by this IQ. + * + * @return the ID of the conference represented by this IQ + */ + public String getID() + { + return id; + } + + public Content getOrCreateContent(String contentName) + { + Content content = getContent(contentName); + + if (content == null) + { + content = new Content(contentName); + addContent(content); + } + + return content; + } + + /** + * Removes a specific {@link Content} instance from the list of + * Content instances included into this conference IQ. + * + * @param content the Content instance to be removed from the list + * of Content instances included into this conference IQ + * @return true if the list of Content instances included + * into this conference IQ has been modified as a result of the + * method call; otherwise, false + */ + public boolean removeContent(Content content) + { + return contents.remove(content); + } + + /** + * Sets the ID of the conference represented by this IQ. + * + * @param id the ID of the conference represented by this IQ + */ + public void setID(String id) + { + this.id = id; + } + + /** + * Represents a channel included into a content of a Jitsi + * VideoBridge conference IQ. + */ + public static class Channel + { + /** + * The XML element name of a channel of a content of a + * Jitsi VideoBridge conference IQ. + */ + public static final String ELEMENT_NAME = "channel"; + + /** + * The XML name of the host attribute of a channel of + * a content of a conference IQ which represents the + * value of the host property of + * CobriConferenceIQ.Channel. + */ + public static final String HOST_ATTR_NAME = "host"; + + /** + * The XML name of the id attribute of a channel of a + * content of a conference IQ which represents the + * value of the id property of + * CobriConferenceIQ.Channel. + */ + public static final String ID_ATTR_NAME = "id"; + + /** + * The XML name of the rtcpport attribute of a channel + * of a content of a conference IQ which represents + * the value of the rtcpPort property of + * CobriConferenceIQ.Channel. + */ + public static final String RTCP_PORT_ATTR_NAME = "rtcpport"; + + /** + * The XML name of the rtpport attribute of a channel + * of a content of a conference IQ which represents + * the value of the rtpPort property of + * CobriConferenceIQ.Channel. + */ + public static final String RTP_PORT_ATTR_NAME = "rtpport"; + + /** + * The host of the channel represented by this instance. + */ + private String host; + + /** + * The ID of the channel represented by this instance. + */ + private String id; + + /** + * The payload-type elements defined by XEP-0167: Jingle RTP + * Sessions associated with this channel. + */ + private final List payloadTypes + = new ArrayList(); + + /** + * The RTCP port of the channel represented by this instance. + */ + private int rtcpPort; + + /** + * The RTP port of the channel represented by this instance. + */ + private int rtpPort; + + /** + * Adds a payload-type element defined by XEP-0167: Jingle RTP + * Sessions to this channel. + * + * @param payloadType the payload-type element to be added to + * this channel + * @return true if the list of payload-type elements + * associated with this channel has been modified as part of + * the method call; otherwise, false + * @throws NullPointerException if the specified payloadType is + * null + */ + public boolean addPayloadType(PayloadTypePacketExtension payloadType) + { + if (payloadType == null) + throw new NullPointerException("payloadType"); + + return + payloadTypes.contains(payloadType) + ? false + : payloadTypes.add(payloadType); + } + + public String getHost() + { + return host; + } + + /** + * Gets the ID of the channel represented by this instance. + * + * @return the ID of the channel represented by this instance + */ + public String getID() + { + return id; + } + + public List getPayloadTypes() + { + return Collections.unmodifiableList(payloadTypes); + } + + public int getRTCPPort() + { + return rtcpPort; + } + + public int getRTPPort() + { + return rtpPort; + } + + /** + * Removes a payload-type element defined by XEP-0167: Jingle + * RTP Sessions from this channel. + * + * @param payloadType the payload-type element to be removed + * from this channel + * @return true if the list of payload-type elements + * associated with this channel has been modified as part of + * the method call; otherwise, false + */ + public boolean removePayloadType(PayloadTypePacketExtension payloadType) + { + return payloadTypes.remove(payloadType); + } + + public void setHost(String host) + { + this.host = host; + } + + /** + * Sets the ID of the channel represented by this instance. + * + * @param id the ID of the channel represented by this instance + */ + public void setID(String id) + { + this.id = id; + } + + public void setRTCPPort(int rtcpPort) + { + this.rtcpPort = rtcpPort; + } + + public void setRTPPort(int rtpPort) + { + this.rtpPort = rtpPort; + } + + public void toXML(StringBuilder xml) + { + xml.append('<').append(ELEMENT_NAME); + + String id = getID(); + + if (id != null) + xml.append(' ').append(ID_ATTR_NAME).append("='").append(id) + .append('\''); + + String host = getHost(); + + if (host != null) + xml.append(' ').append(HOST_ATTR_NAME).append("='").append(host) + .append('\''); + + int rtpPort = getRTPPort(); + + if (rtpPort > 0) + xml.append(' ').append(RTP_PORT_ATTR_NAME).append("='") + .append(rtpPort).append('\''); + + int rtcpPort = getRTCPPort(); + + if (rtcpPort > 0) + xml.append(' ').append(RTCP_PORT_ATTR_NAME).append("='") + .append(rtcpPort).append('\''); + + List payloadTypes = getPayloadTypes(); + + if (payloadTypes.size() == 0) + { + xml.append(" />"); + } + else + { + xml.append('>'); + for (PayloadTypePacketExtension payloadType : payloadTypes) + xml.append(payloadType.toXML()); + xml.append("'); + } + } + } + + /** + * Represents a content included into a Jitsi VideoBridge + * conference IQ. + */ + public static class Content + { + /** + * The XML element name of a content of a Jitsi VideoBridge + * conference IQ. + */ + public static final String ELEMENT_NAME = "content"; + + /** + * The XML name of the name attribute of a content of + * a conference IQ which represents the name property + * of CobriConferenceIQ.Content. + */ + public static final String NAME_ATTR_NAME = "name"; + + /** + * The list of {@link Channel}s included into this content of a + * conference IQ. + */ + private final List channels = new LinkedList(); + + /** + * The name of the content represented by this instance. + */ + private String name; + + /** + * Initializes a new Content instance without a name and + * channels. + */ + public Content() + { + } + + /** + * Initializes a new Content instance with a specific name and + * without channels. + * + * @param name the name to initialize the new instance with + */ + public Content(String name) + { + setName(name); + } + + public boolean addChannel(Channel channel) + { + if (channel == null) + throw new NullPointerException("channel"); + + return channels.contains(channel) ? false : channels.add(channel); + } + + public Channel getChannel(int channelIndex) + { + return getChannels().get(channelIndex); + } + + public Channel getChannel(String channelID) + { + for (Channel channel : getChannels()) + if (channelID.equals(channel.getID())) + return channel; + return null; + } + + public int getChannelCount() + { + return getChannels().size(); + } + + public List getChannels() + { + return Collections.unmodifiableList(channels); + } + + /** + * Gets the name of the content represented by this instance. + * + * @return the name of the content represented by this instance + */ + public String getName() + { + return name; + } + + public boolean removeChannel(Channel channel) + { + return channels.remove(channel); + } + + /** + * Sets the name of the content represented by this instance. + * + * @param name the name of the content represented by this + * instance + * @throws NullPointerException if the specified name is + * null + */ + public void setName(String name) + { + if (name == null) + throw new NullPointerException("name"); + + this.name = name; + } + + public void toXML(StringBuilder xml) + { + xml.append('<').append(ELEMENT_NAME); + xml.append(' ').append(NAME_ATTR_NAME).append("='") + .append(getName()).append('\''); + + List channels = getChannels(); + + if (channels.size() == 0) + { + xml.append(" />"); + } + else + { + xml.append('>'); + for (Channel channel : channels) + channel.toXML(xml); + xml.append("'); + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriIQProvider.java new file mode 100644 index 000000000..95484cbf4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriIQProvider.java @@ -0,0 +1,187 @@ +/* + * Jitsi, 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.jabber.extensions.cobri; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; +import org.xmlpull.v1.*; + +/** + * Implements an org.jivesoftware.smack.provider.IQProvider for the + * Jitsi VideoBridge extension CobriConferenceIQ. + * + * @author Lyubomir Marinov + */ +public class CobriIQProvider + implements IQProvider +{ + /** + * Parses an IQ sub-document and creates an + * org.jivesoftware.smack.packet.IQ instance. + * + * @param parser an XmlPullParser which specifies the IQ + * sub-document to be parsed into a new IQ instance + * @return a new IQ instance parsed from the specified IQ + * sub-document + * @see IQProvider#parseIQ(XmlPullParser) + */ + public IQ parseIQ(XmlPullParser parser) + throws Exception + { + String namespace = parser.getNamespace(); + IQ iq; + + if (CobriConferenceIQ.ELEMENT_NAME.equals(parser.getName()) + && CobriConferenceIQ.NAMESPACE.equals(namespace)) + { + CobriConferenceIQ conference = new CobriConferenceIQ(); + String conferenceID + = parser.getAttributeValue("", CobriConferenceIQ.ID_ATTR_NAME); + + if ((conferenceID != null) && (conferenceID.length() != 0)) + conference.setID(conferenceID); + + boolean done = false; + CobriConferenceIQ.Channel channel = null; + CobriConferenceIQ.Content content = null; + PacketExtensionProvider payloadTypePacketExtensionProvider = null; + + while (!done) + { + switch (parser.next()) + { + case XmlPullParser.END_TAG: + { + String name = parser.getName(); + + if (CobriConferenceIQ.ELEMENT_NAME.equals(name)) + { + done = true; + } + else if (CobriConferenceIQ.Channel.ELEMENT_NAME + .equals(name)) + { + content.addChannel(channel); + channel = null; + } + else if (CobriConferenceIQ.Content.ELEMENT_NAME + .equals(name)) + { + conference.addContent(content); + content = null; + } + break; + } + + case XmlPullParser.START_TAG: + { + String name = parser.getName(); + + if (CobriConferenceIQ.Channel.ELEMENT_NAME.equals(name)) + { + channel = new CobriConferenceIQ.Channel(); + + String channelID + = parser.getAttributeValue( + "", + CobriConferenceIQ.Channel.ID_ATTR_NAME); + + if ((channelID != null) && (channelID.length() != 0)) + channel.setID(channelID); + + String host + = parser.getAttributeValue( + "", + CobriConferenceIQ.Channel.HOST_ATTR_NAME); + + if ((host != null) && (host.length() != 0)) + channel.setHost(host); + + String rtpPort + = parser.getAttributeValue( + "", + CobriConferenceIQ.Channel + .RTP_PORT_ATTR_NAME); + + if ((rtpPort != null) && (rtpPort.length() != 0)) + channel.setRTPPort(Integer.parseInt(rtpPort)); + + String rtcpPort + = parser.getAttributeValue( + "", + CobriConferenceIQ.Channel + .RTCP_PORT_ATTR_NAME); + + if ((rtcpPort != null) && (rtcpPort.length() != 0)) + channel.setRTCPPort(Integer.parseInt(rtcpPort)); + + } + else if (CobriConferenceIQ.Content.ELEMENT_NAME + .equals(name)) + { + content = new CobriConferenceIQ.Content(); + + String contentName + = parser.getAttributeValue( + "", + CobriConferenceIQ.Content.NAME_ATTR_NAME); + + if ((contentName != null) + && (contentName.length() != 0)) + content.setName(contentName); + } + else if (PayloadTypePacketExtension.ELEMENT_NAME.equals( + name)) + { + /* + * The channel element of the Jitsi VideoBridge protocol + * reuses the payload-type element defined in XEP-0167: + * Jingle RTP Sessions. + */ + if (payloadTypePacketExtensionProvider == null) + { + payloadTypePacketExtensionProvider + = (PacketExtensionProvider) + ProviderManager.getInstance() + .getExtensionProvider(name, namespace); + } + if (payloadTypePacketExtensionProvider == null) + { + /* + * Well, the PacketExtensionProvider appears to have + * not been registered. Throw away the payload-type + * element. + */ + while ((XmlPullParser.END_TAG != parser.next()) + || !name.equals(parser.getName())); + } + else + { + PayloadTypePacketExtension payloadType + = (PayloadTypePacketExtension) + payloadTypePacketExtensionProvider + .parseExtension(parser); + + if (payloadType != null) + channel.addPayloadType(payloadType); + } + } + break; + } + } + } + + iq = conference; + } + else + iq = null; + + return iq; + } +}