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