001/**
002 *
003 * Copyright 2003-2005 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.jingle.packet;
018
019import org.jivesoftware.smack.packet.PacketExtension;
020import org.jivesoftware.smackx.jingle.nat.ICECandidate;
021import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
022
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.Iterator;
026import java.util.List;
027
028/**
029 * A jingle transport extension
030 *
031 * @author Alvaro Saurin <alvaro.saurin@gmail.com>
032 */
033public class JingleTransport implements PacketExtension {
034
035    // static
036
037    public static final String NODENAME = "transport";
038
039    // non-static
040
041    protected String namespace;
042
043    protected final List<JingleTransportCandidate> candidates = new ArrayList<JingleTransportCandidate>();
044
045    /**
046     * Default constructor.
047     */
048    public JingleTransport() {
049        super();
050    }
051
052    /**
053     * Utility constructor, with a transport candidate element.
054     *
055     * @param candidate A transport candidate element to add.
056     */
057    public JingleTransport(final JingleTransportCandidate candidate) {
058        super();
059        addCandidate(candidate);
060    }
061
062    /**
063     * Copy constructor.
064     *
065     * @param tr the other jingle transport.
066     */
067    public JingleTransport(final JingleTransport tr) {
068        if (tr != null) {
069            namespace = tr.namespace;
070
071            if (tr.candidates.size() > 0) {
072                candidates.addAll(tr.candidates);
073            }
074        }
075    }
076
077    /**
078     * Adds a transport candidate.
079     *
080     * @param candidate the candidate
081     */
082    public void addCandidate(final JingleTransportCandidate candidate) {
083        if (candidate != null) {
084            synchronized (candidates) {
085                candidates.add(candidate);
086            }
087        }
088    }
089
090    /**
091     * Get an iterator for the candidates
092     *
093     * @return an iterator
094     */
095    public Iterator<JingleTransportCandidate> getCandidates() {
096        return Collections.unmodifiableList(getCandidatesList()).iterator();
097    }
098
099    /**
100     * Get the list of candidates.
101     *
102     * @return The candidates list.
103     */
104    public List<JingleTransportCandidate> getCandidatesList() {
105        ArrayList<JingleTransportCandidate> res = null;
106        synchronized (candidates) {
107            res = new ArrayList<JingleTransportCandidate>(candidates);
108        }
109        return res;
110    }
111
112    /**
113     * Get the number of transport candidates.
114     *
115     * @return The number of transport candidates contained.
116     */
117    public int getCandidatesCount() {
118        return getCandidatesList().size();
119    }
120
121    /**
122     * Returns the XML element name of the element.
123     *
124     * @return the XML element name of the element.
125     */
126    public String getElementName() {
127        return NODENAME;
128    }
129
130    /**
131     * Set the namespace.
132     *
133     * @param ns The namespace
134     */
135    protected void setNamespace(final String ns) {
136        namespace = ns;
137    }
138
139    /**
140     * Get the namespace.
141     *
142     * @return The namespace
143     */
144    public String getNamespace() {
145        return namespace;
146    }
147
148    /**
149     * Return the XML representation for this element.
150     */
151    public String toXML() {
152        StringBuilder buf = new StringBuilder();
153
154        buf.append("<").append(getElementName()).append(" xmlns=\"");
155        buf.append(getNamespace()).append("\" ");
156
157        synchronized (candidates) {
158            if (getCandidatesCount() > 0) {
159                buf.append(">");
160                Iterator<JingleTransportCandidate> iter = getCandidates();
161
162                while (iter.hasNext()) {
163                    JingleTransportCandidate candidate = iter.next();
164                    buf.append(candidate.toXML());
165                }
166                buf.append("</").append(getElementName()).append(">");
167            } else {
168                buf.append("/>");
169            }
170        }
171
172        return buf.toString();
173    }
174
175    /**
176     * Candidate element in the transport. This class acts as a view of the
177     * "TransportCandidate" in the Jingle space.
178     *
179     * @author Alvaro Saurin
180     * @see TransportCandidate
181     */
182    public static abstract class JingleTransportCandidate {
183
184        public static final String NODENAME = "candidate";
185
186        // The transport candidate contained in the element.
187        protected TransportCandidate transportCandidate;
188
189        /**
190         * Creates a new TransportNegotiator child.
191         */
192        public JingleTransportCandidate() {
193            super();
194        }
195
196        /**
197         * Creates a new TransportNegotiator child.
198         *
199         * @param candidate the jmf transport candidate
200         */
201        public JingleTransportCandidate(final TransportCandidate candidate) {
202            super();
203            setMediaTransport(candidate);
204        }
205
206        /**
207         * Returns the XML element name of the element.
208         *
209         * @return the XML element name of the element.
210         */
211        public static String getElementName() {
212            return NODENAME;
213        }
214
215        /**
216         * Get the current transportElement candidate.
217         *
218         * @return the transportElement candidate
219         */
220        public TransportCandidate getMediaTransport() {
221            return transportCandidate;
222        }
223
224        /**
225         * Set the transportElement candidate.
226         *
227         * @param cand the transportElement candidate
228         */
229        public void setMediaTransport(final TransportCandidate cand) {
230            if (cand != null) {
231                transportCandidate = cand;
232            }
233        }
234
235        /**
236         * Get the list of attributes.
237         *
238         * @return a string with the list of attributes.
239         */
240        protected String getChildElements() {
241            return null;
242        }
243
244        /**
245         * Obtain a valid XML representation of a trancport candidate
246         *
247         * @return A string containing the XML dump of the transport candidate.
248         */
249        public String toXML() {
250            StringBuilder buf = new StringBuilder();
251            String childElements = getChildElements();
252
253            if (transportCandidate != null && childElements != null) {
254                buf.append("<").append(getElementName()).append(" ");
255                buf.append(childElements);
256                buf.append("/>");
257            }
258
259            return buf.toString();
260        }
261    }
262
263    // Subclasses
264
265    /**
266     * RTP-ICE profile
267     */
268    public static class Ice extends JingleTransport {
269        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:transports:ice-udp";
270
271        public Ice() {
272            super();
273            setNamespace(NAMESPACE);
274        }
275
276        /**
277         * Add a transport candidate
278         *
279         * @see org.jivesoftware.smackx.jingle.packet.JingleTransport#addCandidate(org.jivesoftware.smackx.jingle.packet.JingleTransport.JingleTransportCandidate)
280         */
281        public void addCandidate(final JingleTransportCandidate candidate) {
282            super.addCandidate(candidate);
283        }
284
285        /**
286         * Get the list of candidates. As a "raw-udp" transport can only contain
287         * one candidate, we use the first in the list...
288         *
289         * @see org.jivesoftware.smackx.jingle.packet.JingleTransport#getCandidates()
290         */
291        public List<JingleTransportCandidate> getCandidatesList() {
292            List<JingleTransportCandidate> copy = new ArrayList<JingleTransportCandidate>();
293            List<JingleTransportCandidate> superCandidatesList = super.getCandidatesList();
294            for (int i = 0; i < superCandidatesList.size(); i++) {
295                copy.add(superCandidatesList.get(i));
296            }
297
298            return copy;
299        }
300
301        public static class Candidate extends JingleTransportCandidate {
302            /**
303             * Default constructor
304             */
305            public Candidate() {
306                super();
307            }
308
309            /**
310             * Constructor with a transport candidate.
311             */
312            public Candidate(final TransportCandidate tc) {
313                super(tc);
314            }
315
316            /**
317             * Get the elements of this candidate.
318             */
319            protected String getChildElements() {
320                StringBuilder buf = new StringBuilder();
321
322                if (transportCandidate != null) {// && transportCandidate instanceof ICECandidate) {
323                    ICECandidate tci = (ICECandidate) transportCandidate;
324
325                    // We convert the transportElement candidate to XML here...
326                    buf.append(" generation=\"").append(tci.getGeneration()).append("\"");
327                    buf.append(" ip=\"").append(tci.getIp()).append("\"");
328                    buf.append(" port=\"").append(tci.getPort()).append("\"");
329                    buf.append(" network=\"").append(tci.getNetwork()).append("\"");
330                    buf.append(" username=\"").append(tci.getUsername()).append("\"");
331                    buf.append(" password=\"").append(tci.getPassword()).append("\"");
332                    buf.append(" preference=\"").append(tci.getPreference()).append("\"");
333                    buf.append(" type=\"").append(tci.getType()).append("\"");
334
335                    // Optional elements
336                    if (transportCandidate.getName() != null) {
337                        buf.append(" name=\"").append(tci.getName()).append("\"");
338                    }
339                }
340
341                return buf.toString();
342            }
343
344        }
345    }
346
347    /**
348     * Raw UDP profile.
349     */
350    public static class RawUdp extends JingleTransport {
351        public static final String NAMESPACE = "http://www.xmpp.org/extensions/xep-0177.html#ns";
352
353        public RawUdp() {
354            super();
355            setNamespace(NAMESPACE);
356        }
357
358        /**
359         * Add a transport candidate
360         *
361         * @see org.jivesoftware.smackx.jingle.packet.JingleTransport#addCandidate(org.jivesoftware.smackx.jingle.packet.JingleTransport.JingleTransportCandidate)
362         */
363        public void addCandidate(final JingleTransportCandidate candidate) {
364            candidates.clear();
365            super.addCandidate(candidate);
366        }
367
368        /**
369         * Get the list of candidates. As a "raw-udp" transport can only contain
370         * one candidate, we use the first in the list...
371         *
372         * @see org.jivesoftware.smackx.jingle.packet.JingleTransport#getCandidates()
373         */
374        public List<JingleTransportCandidate> getCandidatesList() {
375            List<JingleTransportCandidate> copy = new ArrayList<JingleTransportCandidate>();
376            List<JingleTransportCandidate> superCandidatesList = super.getCandidatesList();
377            if (superCandidatesList.size() > 0) {
378                copy.add(superCandidatesList.get(0));
379            }
380
381            return copy;
382        }
383
384        /**
385         * Raw-udp transport candidate.
386         */
387        public static class Candidate extends JingleTransportCandidate {
388            /**
389             * Default constructor
390             */
391            public Candidate() {
392                super();
393            }
394
395            /**
396             * Constructor with a transport candidate.
397             */
398            public Candidate(final TransportCandidate tc) {
399                super(tc);
400            }
401
402            /**
403             * Get the elements of this candidate.
404             */
405            protected String getChildElements() {
406                StringBuilder buf = new StringBuilder();
407
408                if (transportCandidate != null && transportCandidate instanceof TransportCandidate.Fixed) {
409                    TransportCandidate.Fixed tcf = (TransportCandidate.Fixed) transportCandidate;
410
411                    buf.append(" generation=\"").append(tcf.getGeneration()).append("\"");
412                    buf.append(" ip=\"").append(tcf.getIp()).append("\"");
413                    buf.append(" port=\"").append(tcf.getPort()).append("\"");
414
415                    // Optional parameters
416                    String name = tcf.getName();
417                    if (name != null) {
418                        buf.append(" name=\"").append(name).append("\"");
419                    }
420                }
421                return buf.toString();
422            }
423
424        }
425    }
426}