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(String 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            public Candidate(final TransportCandidate tc) {
319                super(tc);
320            }
321
322            /**
323             * Get the elements of this candidate.
324             */
325            @Override
326            protected String getChildElements() {
327                StringBuilder buf = new StringBuilder();
328
329                if (transportCandidate != null) { // && transportCandidate instanceof ICECandidate) {
330                    ICECandidate tci = (ICECandidate) transportCandidate;
331
332                    // We convert the transportElement candidate to XML here...
333                    buf.append(" generation=\"").append(tci.getGeneration()).append('"');
334                    buf.append(" ip=\"").append(tci.getIp()).append('"');
335                    buf.append(" port=\"").append(tci.getPort()).append('"');
336                    buf.append(" network=\"").append(tci.getNetwork()).append('"');
337                    buf.append(" username=\"").append(tci.getUsername()).append('"');
338                    buf.append(" password=\"").append(tci.getPassword()).append('"');
339                    buf.append(" preference=\"").append(tci.getPreference()).append('"');
340                    buf.append(" type=\"").append(tci.getType()).append('"');
341
342                    // Optional elements
343                    if (transportCandidate.getName() != null) {
344                        buf.append(" name=\"").append(tci.getName()).append('"');
345                    }
346                }
347
348                return buf.toString();
349            }
350
351        }
352    }
353
354    /**
355     * Raw UDP profile.
356     */
357    public static class RawUdp extends JingleTransport {
358        public static final String NAMESPACE = "http://www.xmpp.org/extensions/xep-0177.html#ns";
359
360        public RawUdp() {
361            super();
362            setNamespace(NAMESPACE);
363        }
364
365        /**
366         * Add a transport candidate.
367         *
368         * @see org.jivesoftware.smackx.jingleold.packet.JingleTransport#addCandidate(org.jivesoftware.smackx.jingleold.packet.JingleTransport.JingleTransportCandidate)
369         */
370        @Override
371        public void addCandidate(final JingleTransportCandidate candidate) {
372            candidates.clear();
373            super.addCandidate(candidate);
374        }
375
376        /**
377         * Get the list of candidates. As a "raw-udp" transport can only contain
378         * one candidate, we use the first in the list...
379         *
380         * @see org.jivesoftware.smackx.jingleold.packet.JingleTransport#getCandidates()
381         */
382        @Override
383        public List<JingleTransportCandidate> getCandidatesList() {
384            List<JingleTransportCandidate> copy = new ArrayList<>();
385            List<JingleTransportCandidate> superCandidatesList = super.getCandidatesList();
386            if (superCandidatesList.size() > 0) {
387                copy.add(superCandidatesList.get(0));
388            }
389
390            return copy;
391        }
392
393        /**
394         * Raw-udp transport candidate.
395         */
396        public static class Candidate extends JingleTransportCandidate {
397            /**
398             * Default constructor.
399             */
400            public Candidate() {
401                super();
402            }
403
404            /**
405             * Constructor with a transport candidate.
406             */
407            public Candidate(final TransportCandidate tc) {
408                super(tc);
409            }
410
411            /**
412             * Get the elements of this candidate.
413             */
414            @Override
415            protected String getChildElements() {
416                StringBuilder buf = new StringBuilder();
417
418                if (transportCandidate != null && transportCandidate instanceof TransportCandidate.Fixed) {
419                    TransportCandidate.Fixed tcf = (TransportCandidate.Fixed) transportCandidate;
420
421                    buf.append(" generation=\"").append(tcf.getGeneration()).append('"');
422                    buf.append(" ip=\"").append(tcf.getIp()).append('"');
423                    buf.append(" port=\"").append(tcf.getPort()).append('"');
424
425                    // Optional parameters
426                    String name = tcf.getName();
427                    if (name != null) {
428                        buf.append(" name=\"").append(name).append('"');
429                    }
430                }
431                return buf.toString();
432            }
433
434        }
435    }
436}