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